﻿Imports System.Text
Imports AvianWaves.AvianPlay.AudioEngine
Imports AvianWaves.AvianPlay.Playlists
Imports AvianWaves.AvianPlay.Language
Imports AvianWaves.AvianPlay.ErrorLog
Imports AvianWaves.AvianPlay.ControlManager
Imports System.Data.SqlServerCe

Public Class AvianPlayMain
  Private WithEvents _AudioEngine As AudioEngine = Nothing
  Private WithEvents _libraryMonitor As Library.Monitor = Nothing
  Private WithEvents _em As EventManager = Nothing
  Private WithEvents _pcm As PodcastManager = Nothing
  Private _db As AvianPlayDB = Nothing
  Private _dsm As New DeviceSleepManager
  Private _lmb As Library.MenuBuilder = Nothing
  Private _smb As SettingsMenuBuilder = Nothing

  Private _libraryRestartScan As Boolean = False

  Private _nowPlayingUpdateThread As Threading.Thread = Nothing
  Private _nowPlayingEntries As New List(Of String)
  Private _thumbnailEntries As New List(Of ThumbnailData)
  Private _thumbnailCache As New List(Of ThumbnailData)

  Private _keyDownHandled As Boolean = False
  Private _keyPressHandled As Boolean = False

  Private _isExiting As Boolean = False
  Private _gracefulExit As Boolean = False

  Private _currentPlaylist As AVPLPlaylist = Nothing
  Private _currentAlbumName As String = String.Empty
  Private _currentTrackNumber As Integer = 0
  Private _musicPlaylist As New AVPLPlaylist
  Private _podcastPlaylist As New AVPLPlaylist
  Private _radioPlaylist As New AVPLPlaylist
  Private _editorPlaylist As New AVPLPlaylist
  Private _cacheSaveThread As Threading.Thread = Nothing
  Private _cacheSaveQueue As New Queue(Of CacheSaveItems)
  Private _cacheSaveQuit As Boolean = False
  Private _checkMemorizeThread As Threading.Thread = Nothing

  Private _contextMenuAVPLE As AVPLPlaylistEntry = Nothing ' When an AVPLE context menu is raised, this temporarily holds the entry that raised it
  Private _contextMenuPlaylist As String = "" ' When a playlist context menu is raised, this temporarily holds the entry that raised it

  Private _lastSmallLabel As Boolean = False
  Private _libraryIsEnabled As Boolean = True

  Private _slidingStripHeight As Integer = 0
  Private _dpiFactor As Single = 1.0F

  Private _buttonStripItems As New MobilePrettyGui.EnumerableInterfaceList(Of BottomStripButton, MobilePrettyGui.iSlidingButtonStripItem)
  Private _musicIndex As Integer = 0
  Private _podcastIndex As Integer = 1
  Private _radioIndex As Integer = 2
  Private _libraryIndex As Integer = 3
  Private _editorIndex As Integer = 4
  Private _settingsIndex As Integer = 5
  Private _currentIndex As Integer  ' The index representing the control surface the user is currently on
  Private _playingIndex As Integer  ' The index representing the control surface that should be used for the file type (music for music, podcast for podcast, etc.)

  Private _prettyListMiddleShouldBeVisible As Boolean = False
  Private _streamStopWatch As New Stopwatch
  Private _streamSongStopWatch As New Stopwatch
  Private _showStatusMessageAlt As Boolean = False
  Private _streamRetryStart As Integer = -2
  Private _streamRetryTicks As Integer = 0
  Private _streamRetryMaxTicks As Integer = 5
  Private _streamRetry As Integer = Me._streamRetryStart
  Private _streamRetryFlushed As Boolean = False
  Private _noItemLoaded As Boolean = True

  Private _libraryScrollPositionSlide As Integer = 0 ' Used only when sliding back and forth
  Private _libraryScrollPosition As New Stack(Of Integer)
  Private _settingScrollPosition As Integer = 0
  Private _settingScrollPositionSlide As Integer = 0
  Private _editorScrollPositionSlide As Integer = 0

  Private _libraryBreadcrumb As New Stack(Of String)
  Private _settingsBreadcrumb As New Stack(Of String)
  Private _nextMenuBreadcrumb As String = String.Empty

  Private _statusBitrate As Integer = 0
  Private _lastIsReady As Boolean = False
  Private _lastNoItemLoaded As Boolean = True
  Private _lastIsWait As Boolean = False
  Private _lastStatus As MAP_STATUS = MAP_STATUS.STOP
  Private _lastStreamStatus As AudioEngine.MAP_STREAMING_STATUS = MAP_STREAMING_STATUS.DISCONNECTED
  Private _lastStreamURLIndex As Integer = 0
  Private _lastMonitorIsUpdating As Boolean = False ' Use this instead of the live value for determining what menu items to show to avoid async problems
  Private _lastPodcastIsUpdating As Boolean = False ' Use this instead of the live value for determining what menu items to show to avoid async problems
  Private _lastPodcastIsDownloading As Boolean = False ' Use this instead of the live value for determining what menu items to show to avoid async problems
  Private _lastPodcastDownloadedBytes As Integer = 0 ' Use this instead of the live value for determining what menu items to show to avoid async problems
  Private _lastPodcastDownloadSize As Integer = 0 ' Use this instead of the live value for determining what menu items to show to avoid async problems
  Private _lastArtist As String = Nothing
  Private _lastTitle As String = Nothing

  Private _lastButtonDownFunc As Integer = 0
  Private _lastButtonDownParam As Integer = 0
  Private _forwardRewindIsRewind As Boolean = False

  Private _longPressStopWatch As New Stopwatch

  Private _isFinishedLoading As Boolean = False

  Public Structure ThumbnailData
    Dim filepath As String
    Dim bmp As Bitmap
  End Structure

  Public Enum CacheSaveItems
    Music = 0
    Podcast = 1
    Radio = 2
    Editor = 3
  End Enum

#Region " Menus "
  ' Items in the Controls display
  Private WithEvents _menuItemControlPlay As New MenuItem
  Private WithEvents _menuItemControlPause As New MenuItem
  Private WithEvents _menuItemControlStop As New MenuItem
  Private WithEvents _menuItemControlClose As New MenuItem
  Private WithEvents _menuItemControlSource As New MenuItem
  Private WithEvents _menuItemControlPrevious As New MenuItem
  Private WithEvents _menuItemControlNext As New MenuItem
  Private WithEvents _menuItemControlRepeat As New MenuItem
  Private WithEvents _menuItemControlShuffle As New MenuItem
  Private WithEvents _menuItemControlNextScene As New MenuItem
  Private WithEvents _menuItemControlSetVolume As New MenuItem

  ' Source menu item cache
  Private _controlSourceSubMenuList As New List(Of MenuItem)

  ' Items in the Now Playing list
  Private WithEvents _menuItemOpenPlaylistFile As New MenuItem
  Private WithEvents _menuItemAddMusicFile As New MenuItem
  Private WithEvents _menuItemClearList As New MenuItem
  Private WithEvents _menuItemOpenInPlaylistEditor As New MenuItem

  ' Items in the library menu
  Private WithEvents _menuItemLibraryRefreshList As New MenuItem
  Private WithEvents _menuItemLibraryRescanStart As New MenuItem
  Private WithEvents _menuItemLibraryRescanCancel As New MenuItem
  Private WithEvents _menuItemLibraryUpdatePodcastsStart As New MenuItem
  Private WithEvents _menuItemLibraryUpdatePodcastsCancel As New MenuItem

  ' Items in the playlist editor menu
  Private WithEvents _menuItemEditorOpen As New MenuItem
  Private WithEvents _menuItemEditorOpenPlaylistFile As New MenuItem
  Private WithEvents _menuItemEditorOpenNPMusic As New MenuItem
  Private WithEvents _menuItemEditorOpenNPPodcast As New MenuItem
  Private WithEvents _menuItemEditorOpenNPRadio As New MenuItem
  Private WithEvents _menuItemEditorSaveAs As New MenuItem
  Private WithEvents _menuItemEditorSendTo As New MenuItem
  Private WithEvents _menuItemEditorSendToNPMusic As New MenuItem
  Private WithEvents _menuItemEditorSendToNPPodcast As New MenuItem
  Private WithEvents _menuItemEditorSendToNPRadio As New MenuItem
  Private WithEvents _menuItemEditorSort As New MenuItem
  Private WithEvents _menuItemEditorSortAlphaL1 As New MenuItem
  Private WithEvents _menuItemEditorSortAlphaL2 As New MenuItem
  Private WithEvents _menuItemEditorSortType As New MenuItem
  Private WithEvents _menuItemEditorReverse As New MenuItem
  Private WithEvents _menuItemEditorAddFile As New MenuItem
  Private WithEvents _menuItemEditorAddStream As New MenuItem
  Private WithEvents _menuItemEditorClearList As New MenuItem

  ' Items in settings menu
  Private WithEvents _menuItemSettingsResetAudioEngine As New MenuItem
  Private WithEvents _menuItemSettingsResetLibraryItemUpdateStatus As New MenuItem
  Private WithEvents _menuItemSettingsResetINIFiles As New MenuItem
  Private WithEvents _menuItemSettingsResetAllFiles As New MenuItem
  Private WithEvents _menuItemSettingsResetControlsFile As New MenuItem
  Private WithEvents _menuItemSettingsResetEventsFile As New MenuItem
  Private WithEvents _menuItemSettingsResetLibraryFile As New MenuItem
  Private WithEvents _menuItemSettingsResetPodcastsFile As New MenuItem
  Private WithEvents _menuItemSettingsResetRegExFile As New MenuItem
  Private WithEvents _menuItemSettingsResetSchemaFile As New MenuItem
  Private WithEvents _menuItemSettingsResetSettingsFile As New MenuItem

  ' Items in every menu
  Private WithEvents _menuItemExit As New MenuItem

  ' Separators
  Private _menuItemSeparator1 As New MenuItem
  Private _menuItemSeparator2 As New MenuItem
  Private _menuItemSeparator3 As New MenuItem
  Private _menuItemSeparator4 As New MenuItem

  Private Sub MenuItemControlPlay_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemControlPlay.Click
    Me.ProcessButtonPress(ControlFunctions.Functions.Play)
  End Sub

  Private Sub MenuItemControlPause_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemControlPause.Click
    Me.ProcessButtonPress(ControlFunctions.Functions.Pause)
  End Sub

  Private Sub MenuItemControlStop_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemControlStop.Click
    Me.ProcessButtonPress(ControlFunctions.Functions.Stop)
  End Sub

  Private Sub MenuItemControlClose_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemControlClose.Click
    Me.ProcessButtonPress(ControlFunctions.Functions.Close)
  End Sub

  Private Sub MenuItemControlSourceSubMenu_Click(ByVal sender As Object, ByVal e As EventArgs)
    Dim index As Integer = -1

    If sender.GetType Is GetType(MenuItem) Then
      Try
        Dim str As String = CType(sender, MenuItem).Text.Split(".")(0)
        index = Integer.Parse(str.Trim) - 1
      Catch ex As Exception
        ErrorLog.WriteToErrorLog("Unable to parse out stream URL index from source menu :: " & ex.ToString)
      End Try
    End If

    If index >= 0 Then
      If Me._AudioEngine.LastButton <> MAP_STATUS.STOP Then
        Me._AudioEngine.Stop(True)
      End If

      Me.LoadNextCurrentPlayingItemStreamURL(index)

      If Me._AudioEngine.LastButton = MAP_STATUS.PLAY Then
        Me._lastStreamStatus = MAP_STREAMING_STATUS.CONNECTING
        Me._AudioEngine.Play()
      End If
    End If
  End Sub

  Private Sub MenuItemControlPrevious_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemControlPrevious.Click
    Me.ProcessButtonPress(ControlFunctions.Functions.PreviousRewind)
  End Sub

  Private Sub MenuItemControlNext_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemControlNext.Click
    Me.ProcessButtonPress(ControlFunctions.Functions.NextForward)
  End Sub

  Private Sub MenuItemControlSetVolume_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemControlSetVolume.Click
    Me.ShowVolumeForm()
  End Sub

  Private Sub MenuItemControlRepeat_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemControlRepeat.Click
    Me._menuItemControlRepeat.Checked = Not Me._menuItemControlRepeat.Checked
  End Sub

  Private Sub MenuItemControlShuffle_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemControlShuffle.Click
    Me._menuItemControlShuffle.Checked = Not Me._menuItemControlShuffle.Checked
    If Me._menuItemControlShuffle.Checked Then
      Me.ReshuffleCurrentPlaylist()
    End If
  End Sub

  Private Sub MenuItemControlNextScene_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemControlNextScene.Click
    Me.NextScene()
  End Sub

  Private Sub MenuItemAddMusicFile_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemAddMusicFile.Click
    Me.OpenFileIntoNowPlaying()
  End Sub

  Private Sub MenuItemOpenPlaylistFile_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemOpenPlaylistFile.Click
    Me.OpenPlaylistIntoNowPlaying()
  End Sub

  Private Sub MenuItemClearList_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemClearList.Click
    Me.ClearItemsInCurrentNowPlayingList()
  End Sub

  Private Sub MenuItemOpenInPlaylistEditor_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemOpenInPlaylistEditor.Click
    If Me.LoadIntoEditorPlaylist(Me.ActiveNowPlayingList()) Then
      Me.SwitchToViewIndexHandler(Me._editorIndex)
    End If
  End Sub

  Private Sub MenuItemLibraryRefreshList_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemLibraryRefreshList.Click
    Me._lmb.RefreshMenu()
    Me.PrettyListMiddle.Items = Me._lmb.Items
  End Sub

  Private Sub MenuItemLibraryRescanStart_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemLibraryRescanStart.Click
    If Not Me._libraryMonitor.IsMonitorFullScanActive Then
      Me._libraryMonitor.RescanMonitoredPaths()
    End If
  End Sub

  Private Sub MenuItemLibraryRescanCancel_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemLibraryRescanCancel.Click
    If Me._libraryMonitor.IsMonitorFullScanActive Then
      Me._libraryMonitor.StopFullScanThread()
    End If
  End Sub

  Private Sub MenuItemLibraryUpdatePodcastsStart_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemLibraryUpdatePodcastsStart.Click
    If Not Me._pcm.IsUpdating Then
      Me._pcm.StartDownloadThread(True)
    End If
  End Sub

  Private Sub MenuItemLibraryUpdatePodcastsCancel_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemLibraryUpdatePodcastsCancel.Click
    If Me._pcm.IsUpdating Then
      Me._pcm.CancelDownloadThread()
    End If
  End Sub

  Private Sub MenuItemExit_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemExit.Click
    If MessageBox.Show(GetLanguageString("ExitAreYouSure"), "Avian Play", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) = Windows.Forms.DialogResult.Yes Then
      Me.Close()
    End If
  End Sub

  Private Sub MenuItemEditorOpenFile(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemEditorOpenPlaylistFile.Click
    Dim avpl As Playlists.AVPLPlaylist = FileSelectionDialogs.GetPlaylistFile()
    If avpl IsNot Nothing Then
      For Each ape As Playlists.AVPLPlaylistEntry In avpl
        Me._editorPlaylist.Entries.Add(ape)
        Me.PrettyListMiddle.Items = Me._editorPlaylist
        Me.SaveNPCacheFiles(CacheSaveItems.Editor)
      Next
    End If
  End Sub

  Private Sub MenuItemEditorOpenNPMusic(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemEditorOpenNPMusic.Click
    Me.LoadIntoEditorPlaylist(Me._musicPlaylist)
  End Sub

  Private Sub MenuItemEditorOpenNPPodcast(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemEditorOpenNPPodcast.Click
    Me.LoadIntoEditorPlaylist(Me._podcastPlaylist)
  End Sub

  Private Sub MenuItemEditorOpenNPRadio(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemEditorOpenNPRadio.Click
    Me.LoadIntoEditorPlaylist(Me._radioPlaylist)
  End Sub

  Private Sub MenuItemEditorSaveAs(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemEditorSaveAs.Click
    If Me._editorPlaylist.Count > 0 Then
      Dim sf As New SelectFile(SelectFile.Mode.FilesAndFolders, GetLanguageString("SavePlaylistFile"))
      If sf.ShowDialog = Windows.Forms.DialogResult.OK Then
        Dim fn As String = sf.Filename
        Dim path As String = sf.Path

        If String.Compare(Microsoft.VisualBasic.Right(fn, 5), ".avpl") <> 0 Then
          fn &= ".avpl"
          path &= ".avpl"
        End If

        If fn.Length <= 5 Then
          MessageBox.Show(GetLanguageString("EditorBadFilename"), "Avian Play", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1)
        Else
          Try
            Dim doWrite As Boolean = True

            If IO.File.Exists(path) Then
              If MessageBox.Show(GetLanguageString("EditorOverwriteExistingPlaylist"), "Avian Play", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) = Windows.Forms.DialogResult.No Then
                doWrite = False
              End If
            End If

            If doWrite Then
              Me._editorPlaylist.SaveToFile(path)
            End If
          Catch ex As Exception
            Dim msg As String = ex.Message
            Dim innerMsg As String = ""

            If ex.InnerException IsNot Nothing Then
              innerMsg = ex.InnerException.Message
            End If

            WriteToErrorLog(msg & "  " & innerMsg)

            If innerMsg.Length > 0 Then
              innerMsg = vbCrLf & vbCrLf & innerMsg
            End If

            MessageBox.Show(GetLanguageString("UnableToSavePlaylist") & " " & msg & innerMsg)
          End Try
        End If
      End If
    Else
      MessageBox.Show(GetLanguageString("EditorCannotSaveEmptyList"), "Avian Play", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1)
    End If
  End Sub

  Private Sub MenuItemEditorSendToNPMusic(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemEditorSendToNPMusic.Click
    Me.LoadFromEditorPlaylist(Me._musicPlaylist)
  End Sub

  Private Sub MenuItemEditorSendToNPPodcast(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemEditorSendToNPPodcast.Click
    Me.LoadFromEditorPlaylist(Me._podcastPlaylist)
  End Sub

  Private Sub MenuItemEditorSendToNPRadio(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemEditorSendToNPRadio.Click
    Me.LoadFromEditorPlaylist(Me._radioPlaylist)
  End Sub

  Private Sub MenuItemEditorSortAlphaL1(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemEditorSortAlphaL1.Click
    Me._editorPlaylist.SortByText()
    Me.PrettyListMiddle.Items = Me._editorPlaylist
    Me.SaveNPCacheFiles(CacheSaveItems.Editor)
  End Sub

  Private Sub MenuItemEditorSortAlphaL2(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemEditorSortAlphaL2.Click
    Me._editorPlaylist.SortBySubtext()
    Me.PrettyListMiddle.Items = Me._editorPlaylist
    Me.SaveNPCacheFiles(CacheSaveItems.Editor)
  End Sub

  Private Sub MenuItemEditorSortType(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemEditorSortType.Click
    Me._editorPlaylist.SortByType()
    Me.PrettyListMiddle.Items = Me._editorPlaylist
    Me.SaveNPCacheFiles(CacheSaveItems.Editor)
  End Sub

  Private Sub MenuItemEditorReverse(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemEditorReverse.Click
    If Me._editorPlaylist.Count > 0 Then
      Dim est As New Stack(Of AVPLPlaylistEntry)

      For Each avple As AVPLPlaylistEntry In Me._editorPlaylist
        est.Push(avple)
      Next

      Me._editorPlaylist.Entries.Clear()

      While est.Count > 0
        Me._editorPlaylist.Entries.Add(est.Pop)
      End While

      Me.PrettyListMiddle.Items = Me._editorPlaylist
      Me.SaveNPCacheFiles(CacheSaveItems.Editor)
    Else
      MessageBox.Show(GetLanguageString("EditorListEmpty"), "Avian Play")
    End If
  End Sub

  Private Sub MenuItemEditorAddFile(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemEditorAddFile.Click
    Dim avple As New Playlists.AVPLPlaylistEntry
    avple.Type = AVPLPlaylistEntry.EntryType.File

    Dim pee As New PlaylistEntryEditor()
    pee.Database = Me._db
    pee.PlaylistEntry = avple
    pee.SetColors(Skin.FormForegroundColor, Skin.FormBackgroundColor, Skin.FormBackgroundColorTextBox)
    If pee.ShowDialog = Windows.Forms.DialogResult.OK Then
      Me._editorPlaylist.Entries.Add(pee.PlaylistEntry)
      Me.PrettyListMiddle.Items = Me._editorPlaylist
      Me.SaveNPCacheFiles(CacheSaveItems.Editor)
    End If
  End Sub

  Private Sub MenuItemEditorAddStream(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemEditorAddStream.Click
    Dim avple As New Playlists.AVPLPlaylistEntry
    avple.Type = AVPLPlaylistEntry.EntryType.Stream

    Dim pee As New PlaylistEntryEditor()
    pee.Database = Me._db
    pee.PlaylistEntry = avple
    pee.SetColors(Skin.FormForegroundColor, Skin.FormBackgroundColor, Skin.FormBackgroundColorTextBox)
    If pee.ShowDialog = Windows.Forms.DialogResult.OK Then
      Me._editorPlaylist.Entries.Add(pee.PlaylistEntry)
      Me.PrettyListMiddle.Items = Me._editorPlaylist
      Me.SaveNPCacheFiles(CacheSaveItems.Editor)
    End If
  End Sub

  Private Sub MenuItemEditorClearList(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemEditorClearList.Click
    Me._editorPlaylist.Entries.Clear()
    Me.PrettyListMiddle.Items = Me._editorPlaylist
    Me.SaveNPCacheFiles(CacheSaveItems.Editor)
  End Sub

  Private Sub MenuItemResetAudioEngine_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemSettingsResetAudioEngine.Click
    Me._AudioEngine.EndAudioEngine()
    Me._AudioEngine.StartAudioEngine()
    Me._AudioEngine.ReOpen()
    MessageBox.Show(GetLanguageString("ResetAudioEngineComplete"), "Avian Play", MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1)
  End Sub

  Private Sub MenuItemResetVariousINIFiles_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemSettingsResetAllFiles.Click, _menuItemSettingsResetControlsFile.Click, _menuItemSettingsResetEventsFile.Click, _menuItemSettingsResetLibraryFile.Click, _menuItemSettingsResetPodcastsFile.Click, _menuItemSettingsResetRegExFile.Click, _menuItemSettingsResetSchemaFile.Click, _menuItemSettingsResetSettingsFile.Click
    If MessageBox.Show(GetLanguageString("ResetINIFilesAreYouSure"), "Avian Play", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) = Windows.Forms.DialogResult.Yes Then
      Try
        If sender Is Me._menuItemSettingsResetAllFiles OrElse sender Is Me._menuItemSettingsResetControlsFile Then
          IO.File.Copy(Settings.INIControlsOriginalPath, Settings.INIControlsPath, True)
        End If
        If sender Is Me._menuItemSettingsResetAllFiles OrElse sender Is Me._menuItemSettingsResetEventsFile Then
          IO.File.Copy(Settings.INIEventsOriginalPath, Settings.INIEventsPath, True)
        End If
        If sender Is Me._menuItemSettingsResetAllFiles OrElse sender Is Me._menuItemSettingsResetLibraryFile Then
          IO.File.Copy(Settings.INILibraryOriginalPath, Settings.INILibraryPath, True)
        End If
        If sender Is Me._menuItemSettingsResetAllFiles OrElse sender Is Me._menuItemSettingsResetPodcastsFile Then
          IO.File.Copy(Settings.INIPodcastsOriginalPath, Settings.INIPodcastsPath, True)
        End If
        If sender Is Me._menuItemSettingsResetAllFiles OrElse sender Is Me._menuItemSettingsResetRegExFile Then
          IO.File.Copy(Settings.INIRegExOriginalPath, Settings.INIRegExPath, True)
        End If
        If sender Is Me._menuItemSettingsResetAllFiles OrElse sender Is Me._menuItemSettingsResetSchemaFile Then
          IO.File.Copy(Settings.INISchemaOriginalPath, Settings.INISchemaPath, True)
        End If
        If sender Is Me._menuItemSettingsResetAllFiles OrElse sender Is Me._menuItemSettingsResetSettingsFile Then
          IO.File.Copy(Settings.INISettingsOriginalPath, Settings.INISettingsPath, True)
        End If

        MessageBox.Show(GetLanguageString("ResetINIFilesComplete"), "Avian Play", MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1)
      Catch ex As Exception
        WriteToErrorLog("MenuItemResetVariousINIFiles :: " & ex.ToString)
        MessageBox.Show(GetLanguageString("ResetINIFilesFailed").Replace("%", ex.Message), "Avian Play", MessageBoxButtons.OK, MessageBoxIcon.Hand, MessageBoxDefaultButton.Button1)
      End Try

      Me.Close()
    End If
  End Sub


  Private Sub MenuItemSettingsResetLibraryItemUpdateStatus_Click(ByVal sender As Object, ByVal e As EventArgs) Handles _menuItemSettingsResetLibraryItemUpdateStatus.Click
    Me._db.SetAllAudioFilesToMinimumModifyDate()
    Me._db.SetAllPlaylistFilesToMinimumModifyDate()
    If Me._lastMonitorIsUpdating Then
      Me._libraryRestartScan = True
      Me._libraryMonitor.StopFullScanThread()
    Else
      Me._libraryMonitor.RescanMonitoredPaths()
    End If
    MessageBox.Show(GetLanguageString("ResetLibraryItemUpdateStatusComplete"), "Avian Play", MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1)
  End Sub

  ''' <summary>
  ''' Rebuilds the dynamic menu items -- you need to do this to reset the language on each item
  ''' </summary>
  Private Sub RebuildMenuItems()
    Me._menuItemSeparator1.Enabled = True
    Me._menuItemSeparator1.Text = "-"
    Me._menuItemSeparator2.Enabled = True
    Me._menuItemSeparator2.Text = "-"
    Me._menuItemSeparator3.Enabled = True
    Me._menuItemSeparator3.Text = "-"
    Me._menuItemSeparator4.Enabled = True
    Me._menuItemSeparator4.Text = "-"


    ' Controls
    Me._menuItemControlPlay.Enabled = True
    Me._menuItemControlPlay.Text = GetLanguageString("MenuControlsPlay")

    Me._menuItemControlPause.Enabled = True
    Me._menuItemControlPause.Text = GetLanguageString("MenuControlsPause")

    Me._menuItemControlStop.Enabled = True
    Me._menuItemControlStop.Text = GetLanguageString("MenuControlsStop")

    Me._menuItemControlClose.Enabled = True
    Me._menuItemControlClose.Text = GetLanguageString("MenuControlsClose")

    Me._menuItemControlPrevious.Enabled = True
    Me._menuItemControlPrevious.Text = GetLanguageString("MenuControlsPrevious")

    Me._menuItemControlNext.Enabled = True
    Me._menuItemControlNext.Text = GetLanguageString("MenuControlsNext")

    Me._menuItemControlSource.Enabled = True
    Me._menuItemControlSource.Text = GetLanguageString("MenuControlsSource")

    Me._menuItemControlSetVolume.Enabled = True
    Me._menuItemControlSetVolume.Text = GetLanguageString("MenuControlsSetVolume")

    Me._menuItemControlRepeat.Enabled = True
    Me._menuItemControlRepeat.Text = GetLanguageString("MenuControlsRepeat")

    Me._menuItemControlShuffle.Enabled = True
    Me._menuItemControlShuffle.Text = GetLanguageString("MenuControlsShuffle")

    Me._menuItemControlNextScene.Enabled = True
    Me._menuItemControlNextScene.Text = GetLanguageString("MenuControlsNextScene")


    ' Now Playing
    Me._menuItemOpenPlaylistFile.Enabled = True
    Me._menuItemOpenPlaylistFile.Text = GetLanguageString("MenuNowPlayingOpenPlaylistFile")

    Me._menuItemAddMusicFile.Enabled = True
    Me._menuItemAddMusicFile.Text = GetLanguageString("MenuNowPlayingAddFile")

    Me._menuItemClearList.Enabled = True
    Me._menuItemClearList.Text = GetLanguageString("MenuNowPlayingClearList")

    Me._menuItemOpenInPlaylistEditor.Enabled = True
    Me._menuItemOpenInPlaylistEditor.Text = GetLanguageString("MenuNowPlayingOpenInPlaylistEditor")


    ' Library
    Me._menuItemLibraryRefreshList.Enabled = True
    Me._menuItemLibraryRefreshList.Text = GetLanguageString("MenuRefreshLibraryList")

    Me._menuItemLibraryRescanStart.Enabled = True
    Me._menuItemLibraryRescanStart.Text = GetLanguageString("MenuUpdateLibrary")

    Me._menuItemLibraryRescanCancel.Enabled = True
    Me._menuItemLibraryRescanCancel.Text = GetLanguageString("MenuUpdateLibraryCancel")

    Me._menuItemLibraryUpdatePodcastsStart.Enabled = True
    Me._menuItemLibraryUpdatePodcastsStart.Text = GetLanguageString("MenuUpdatePodcast")

    Me._menuItemLibraryUpdatePodcastsCancel.Enabled = True
    Me._menuItemLibraryUpdatePodcastsCancel.Text = GetLanguageString("MenuUpdatePodcastCancel")

    Me._menuItemEditorOpen.Enabled = True
    Me._menuItemEditorOpen.Text = GetLanguageString("MenuEditorOpen")

    Me._menuItemEditorOpenPlaylistFile.Enabled = True
    Me._menuItemEditorOpenPlaylistFile.Text = GetLanguageString("MenuEditorOpenPlaylistFile")

    Me._menuItemEditorOpenNPMusic.Enabled = True
    Me._menuItemEditorOpenNPMusic.Text = GetLanguageString("MenuEditorOpenNPMusic")

    Me._menuItemEditorOpenNPPodcast.Enabled = True
    Me._menuItemEditorOpenNPPodcast.Text = GetLanguageString("MenuEditorOpenNPPodcast")

    Me._menuItemEditorOpenNPRadio.Enabled = True
    Me._menuItemEditorOpenNPRadio.Text = GetLanguageString("MenuEditorOpenNPRadio")

    Me._menuItemEditorSaveAs.Enabled = True
    Me._menuItemEditorSaveAs.Text = GetLanguageString("MenuEditorSaveAs")

    Me._menuItemEditorSendTo.Enabled = True
    Me._menuItemEditorSendTo.Text = GetLanguageString("MenuEditorSendTo")

    Me._menuItemEditorSendToNPMusic.Enabled = True
    Me._menuItemEditorSendToNPMusic.Text = GetLanguageString("MenuEditorSendToNPMusic")

    Me._menuItemEditorSendToNPPodcast.Enabled = True
    Me._menuItemEditorSendToNPPodcast.Text = GetLanguageString("MenuEditorSendToNPPodcast")

    Me._menuItemEditorSendToNPRadio.Enabled = True
    Me._menuItemEditorSendToNPRadio.Text = GetLanguageString("MenuEditorSendToNPRadio")

    Me._menuItemEditorSort.Enabled = True
    Me._menuItemEditorSort.Text = GetLanguageString("MenuEditorSort")

    Me._menuItemEditorSortAlphaL1.Enabled = True
    Me._menuItemEditorSortAlphaL1.Text = GetLanguageString("MenuEditorSortAlphaLine1")

    Me._menuItemEditorSortAlphaL2.Enabled = True
    Me._menuItemEditorSortAlphaL2.Text = GetLanguageString("MenuEditorSortAlphaLine2")

    Me._menuItemEditorSortType.Enabled = True
    Me._menuItemEditorSortType.Text = GetLanguageString("MenuEditorSortType")

    Me._menuItemEditorReverse.Enabled = True
    Me._menuItemEditorReverse.Text = GetLanguageString("MenuEditorReverse")

    Me._menuItemEditorAddFile.Enabled = True
    Me._menuItemEditorAddFile.Text = GetLanguageString("MenuEditorAddFile")

    Me._menuItemEditorAddStream.Enabled = True
    Me._menuItemEditorAddStream.Text = GetLanguageString("MenuEditorAddStream")

    Me._menuItemEditorClearList.Enabled = True
    Me._menuItemEditorClearList.Text = GetLanguageString("MenuEditorClearList")


    ' Settings
    Me._menuItemSettingsResetAudioEngine.Enabled = True
    Me._menuItemSettingsResetAudioEngine.Text = GetLanguageString("MenuSettingsResetAudioEngine")

    Me._menuItemSettingsResetLibraryItemUpdateStatus.Enabled = True
    Me._menuItemSettingsResetLibraryItemUpdateStatus.Text = GetLanguageString("MenuSettingsResetLibraryItemUpdateStatus")

    Me._menuItemSettingsResetINIFiles.Enabled = True
    Me._menuItemSettingsResetINIFiles.Text = GetLanguageString("MenuSettingsResetINIFiles")

    Me._menuItemSettingsResetAllFiles.Enabled = True
    Me._menuItemSettingsResetAllFiles.Text = GetLanguageString("MenuSettingsResetAllFiles")

    Me._menuItemSettingsResetControlsFile.Enabled = True
    Me._menuItemSettingsResetControlsFile.Text = GetLanguageString("MenuSettingsResetControlsFile")

    Me._menuItemSettingsResetEventsFile.Enabled = True
    Me._menuItemSettingsResetEventsFile.Text = GetLanguageString("MenuSettingsResetEventsFile")

    Me._menuItemSettingsResetLibraryFile.Enabled = True
    Me._menuItemSettingsResetLibraryFile.Text = GetLanguageString("MenuSettingsResetLibraryFile")

    Me._menuItemSettingsResetPodcastsFile.Enabled = True
    Me._menuItemSettingsResetPodcastsFile.Text = GetLanguageString("MenuSettingsResetPodcastsFile")

    Me._menuItemSettingsResetRegExFile.Enabled = True
    Me._menuItemSettingsResetRegExFile.Text = GetLanguageString("MenuSettingsResetRegExFile")

    Me._menuItemSettingsResetSchemaFile.Enabled = True
    Me._menuItemSettingsResetSchemaFile.Text = GetLanguageString("MenuSettingsResetSchemaFile")

    Me._menuItemSettingsResetSettingsFile.Enabled = True
    Me._menuItemSettingsResetSettingsFile.Text = GetLanguageString("MenuSettingsResetSettingsFile")


    ' General
    Me._menuItemExit.Enabled = True
    Me._menuItemExit.Text = GetLanguageString("MenuExit")


    ' Context menus
    Me.ContextMenuItemNowPlayingEditItem.Text = GetLanguageString("ContextMenuEditItem")
    Me.ContextMenuItemNowPlayingRemoveItem.Text = GetLanguageString("ContextMenuRemoveItem")
    Me.ContextMenuItemNowPlayingRefreshStreamURLs.Text = GetLanguageString("ContextMenuRefreshStreamURLs")
    Me.ContextMenuItemNowPlayingMoveUp.Text = GetLanguageString("ContextMenuMoveUp")
    Me.ContextMenuItemNowPlayingMoveDown.Text = GetLanguageString("ContextMenuMoveDown")

    Me.ContextMenuItemEditorRemoveItem.Text = GetLanguageString("ContextMenuRemoveItem")
    Me.ContextMenuItemEditorMoveUp.Text = GetLanguageString("ContextMenuMoveUp")
    Me.ContextMenuItemEditorMoveDown.Text = GetLanguageString("ContextMenuMoveDown")

    Me.ContextMenuItemLibrarySendToMusic.Text = GetLanguageString("ContextMenuSendToMusic")
    Me.ContextMenuItemLibrarySendToPodcast.Text = GetLanguageString("ContextMenuSendToPodcast")
    Me.ContextMenuItemLibrarySendToRadio.Text = GetLanguageString("ContextMenuSendToRadio")
    Me.ContextMenuItemLibrarySendToEditor.Text = GetLanguageString("ContextMenuSendToEditor")
    Me.ContextMenuItemLibraryIsFavorite.Text = GetLanguageString("ContextMenuFileFavorite")

    Me.ContextMenuLibraryPlaylistItemIsFavorite.Text = GetLanguageString("ContextMenuPlaylistFavorite")


    ' Refresh playback control states (this refreshes the menu items' enabled statuses, if some are disabled)
    Me.RefreshPlaybackStates()
  End Sub

  Private Sub SetMenuMusic(ByVal destination As MenuItem.MenuItemCollection)
    Me.SetMenuControls(destination)
  End Sub

  Private Sub SetMenuPodcast(ByVal destination As MenuItem.MenuItemCollection)
    Me.SetMenuControls(destination)
  End Sub

  Private Sub SetMenuRadio(ByVal destination As MenuItem.MenuItemCollection)
    Me.SetMenuControls(destination)
  End Sub

  Private Sub SetMenuControls(ByVal destination As MenuItem.MenuItemCollection)
    destination.Clear()
    destination.Add(Me._menuItemControlPlay)
    destination.Add(Me._menuItemControlPause)
    destination.Add(Me._menuItemControlStop)
    destination.Add(Me._menuItemControlClose)
    destination.Add(Me._menuItemControlPrevious)
    destination.Add(Me._menuItemControlNext)
    destination.Add(Me._menuItemSeparator1)
    destination.Add(Me._menuItemControlSource)
    destination.Add(Me._menuItemControlSetVolume)
    destination.Add(Me._menuItemControlRepeat)
    destination.Add(Me._menuItemControlShuffle)
    destination.Add(Me._menuItemControlNextScene)
    destination.Add(Me._menuItemSeparator2)
    destination.Add(Me._menuItemExit)
  End Sub

  Private Sub SetMenuNowPlaying(ByVal destination As MenuItem.MenuItemCollection, ByVal isStreamList As Boolean)
    destination.Clear()
    If Me.ActiveNowPlayingList IsNot Me._radioPlaylist Then destination.Add(Me._menuItemAddMusicFile) ' Don't have the 'add file' in the radio playlist
    destination.Add(Me._menuItemOpenPlaylistFile)
    destination.Add(Me._menuItemClearList)
    destination.Add(Me._menuItemSeparator1)
    destination.Add(Me._menuItemOpenInPlaylistEditor)
    destination.Add(Me._menuItemSeparator2)
    destination.Add(Me._menuItemExit)
  End Sub

  Private Sub SetMenuLibrary(ByVal destination As MenuItem.MenuItemCollection)
    destination.Clear()
    destination.Add(Me._menuItemLibraryRefreshList)
    If Me._lastMonitorIsUpdating Then
      destination.Add(Me._menuItemLibraryRescanCancel)
    Else
      destination.Add(Me._menuItemLibraryRescanStart)
    End If
    If Me._lastPodcastIsUpdating Then
      destination.Add(Me._menuItemLibraryUpdatePodcastsCancel)
    Else
      destination.Add(Me._menuItemLibraryUpdatePodcastsStart)
    End If
    destination.Add(Me._menuItemSeparator1)
    destination.Add(Me._menuItemExit)
  End Sub

  Private Sub SetMenuEditor(ByVal destination As MenuItem.MenuItemCollection)
    Me._menuItemEditorOpen.MenuItems.Clear()
    Me._menuItemEditorSendTo.MenuItems.Clear()
    Me._menuItemEditorSort.MenuItems.Clear()
    destination.Clear()

    destination.Add(Me._menuItemEditorOpen)
    Me._menuItemEditorOpen.MenuItems.Add(Me._menuItemEditorOpenPlaylistFile)
    Me._menuItemEditorOpen.MenuItems.Add(Me._menuItemEditorOpenNPMusic)
    Me._menuItemEditorOpen.MenuItems.Add(Me._menuItemEditorOpenNPPodcast)
    Me._menuItemEditorOpen.MenuItems.Add(Me._menuItemEditorOpenNPRadio)
    destination.Add(Me._menuItemEditorSaveAs)
    destination.Add(Me._menuItemEditorSendTo)
    Me._menuItemEditorSendTo.MenuItems.Add(Me._menuItemEditorSendToNPMusic)
    Me._menuItemEditorSendTo.MenuItems.Add(Me._menuItemEditorSendToNPPodcast)
    Me._menuItemEditorSendTo.MenuItems.Add(Me._menuItemEditorSendToNPRadio)
    destination.Add(Me._menuItemSeparator1)
    destination.Add(Me._menuItemEditorSort)
    Me._menuItemEditorSort.MenuItems.Add(Me._menuItemEditorSortAlphaL1)
    Me._menuItemEditorSort.MenuItems.Add(Me._menuItemEditorSortAlphaL2)
    Me._menuItemEditorSort.MenuItems.Add(Me._menuItemEditorSortType)
    destination.Add(Me._menuItemEditorReverse)
    destination.Add(Me._menuItemSeparator2)
    destination.Add(Me._menuItemEditorAddFile)
    destination.Add(Me._menuItemEditorAddStream)
    destination.Add(Me._menuItemEditorClearList)
    destination.Add(Me._menuItemSeparator3)
    destination.Add(Me._menuItemExit)
  End Sub

  Private Sub SetMenuSettings(ByVal destination As MenuItem.MenuItemCollection)
    Me._menuItemSettingsResetINIFiles.MenuItems.Clear()
    destination.Clear()

    destination.Add(Me._menuItemSettingsResetAudioEngine)
    destination.Add(Me._menuItemSettingsResetLibraryItemUpdateStatus)
    destination.Add(Me._menuItemSettingsResetINIFiles)
    destination.Add(Me._menuItemSeparator1)
    destination.Add(Me._menuItemExit)

    Me._menuItemSettingsResetINIFiles.MenuItems.Add(Me._menuItemSettingsResetAllFiles)
    Me._menuItemSettingsResetINIFiles.MenuItems.Add(Me._menuItemSettingsResetControlsFile)
    Me._menuItemSettingsResetINIFiles.MenuItems.Add(Me._menuItemSettingsResetEventsFile)
    Me._menuItemSettingsResetINIFiles.MenuItems.Add(Me._menuItemSettingsResetLibraryFile)
    Me._menuItemSettingsResetINIFiles.MenuItems.Add(Me._menuItemSettingsResetPodcastsFile)
    Me._menuItemSettingsResetINIFiles.MenuItems.Add(Me._menuItemSettingsResetRegExFile)
    Me._menuItemSettingsResetINIFiles.MenuItems.Add(Me._menuItemSettingsResetSchemaFile)
    Me._menuItemSettingsResetINIFiles.MenuItems.Add(Me._menuItemSettingsResetSettingsFile)
  End Sub

  Private Sub ContextMenuLibraryAVPLItem_Popup(ByVal sender As Object, ByVal e As EventArgs) Handles ContextMenuLibraryAVPLItem.Popup
    If Me._contextMenuAVPLE IsNot Nothing Then
      Me.ContextMenuItemLibrarySendToMusic.Enabled = Me.IsMusicTabVisible
      Me.ContextMenuItemLibrarySendToPodcast.Enabled = Me.IsPodcastTabVisible
      Me.ContextMenuItemLibrarySendToRadio.Enabled = Me.IsRadioTabVisible
      Me.ContextMenuItemLibrarySendToEditor.Enabled = True
      Me.ContextMenuItemLibraryIsFavorite.Enabled = True
      Me.ContextMenuItemLibraryIsFavorite.Checked = Me._db.IsAudioFileFavorite(Me._contextMenuAVPLE.FilePath)
    Else
      Me.ContextMenuItemLibrarySendToMusic.Enabled = False
      Me.ContextMenuItemLibrarySendToPodcast.Enabled = False
      Me.ContextMenuItemLibrarySendToRadio.Enabled = False
      Me.ContextMenuItemLibrarySendToEditor.Enabled = False
      Me.ContextMenuItemLibraryIsFavorite.Enabled = False
    End If
  End Sub

  Private Sub ContextMenuItemLibrarySendToMusic_Click(ByVal sender As Object, ByVal e As EventArgs) Handles ContextMenuItemLibrarySendToMusic.Click
    If Me._contextMenuAVPLE IsNot Nothing Then
      Me._musicPlaylist.Entries.Add(New AVPLPlaylistEntry(Me._contextMenuAVPLE))
      Me.SaveNPCacheFiles(CacheSaveItems.Music)
    End If
  End Sub

  Private Sub ContextMenuItemLibrarySendToPodcast_Click(ByVal sender As Object, ByVal e As EventArgs) Handles ContextMenuItemLibrarySendToPodcast.Click
    If Me._contextMenuAVPLE IsNot Nothing Then
      Me._podcastPlaylist.Entries.Add(New AVPLPlaylistEntry(Me._contextMenuAVPLE))
      Me.SaveNPCacheFiles(CacheSaveItems.Podcast)
    End If
  End Sub

  Private Sub ContextMenuItemLibrarySendToRadio_Click(ByVal sender As Object, ByVal e As EventArgs) Handles ContextMenuItemLibrarySendToRadio.Click
    If Me._contextMenuAVPLE IsNot Nothing Then
      Me._radioPlaylist.Entries.Add(New AVPLPlaylistEntry(Me._contextMenuAVPLE))
      Me.SaveNPCacheFiles(CacheSaveItems.Radio)
    End If
  End Sub

  Private Sub ContextMenuItemLibrarySendToEditor_Click(ByVal sender As Object, ByVal e As EventArgs) Handles ContextMenuItemLibrarySendToEditor.Click
    If Me._contextMenuAVPLE IsNot Nothing Then
      Me._editorPlaylist.Entries.Add(New AVPLPlaylistEntry(Me._contextMenuAVPLE))
      Me.SaveNPCacheFiles(CacheSaveItems.Editor)
    End If
  End Sub

  Private Sub ContextMenuItemLibraryIsFavorite_Click(ByVal sender As Object, ByVal e As EventArgs) Handles ContextMenuItemLibraryIsFavorite.Click
    If Me._contextMenuAVPLE IsNot Nothing Then
      ' Flip the favorite flag
      Me._db.IsAudioFileFavorite(Me._contextMenuAVPLE.FilePath) = Not Me.ContextMenuItemLibraryIsFavorite.Checked

      ' Refresh the library menu builder because if we are in the favorites list, this entry might change
      Me._lmb.RefreshMenu()
      Me.PrettyListMiddle.Items = Me._lmb.Items
    End If
  End Sub



  Private Sub ContextMenuLibraryPlaylistItem_Popup(ByVal sender As Object, ByVal e As EventArgs) Handles ContextMenuLibraryPlaylistItem.Popup
    If Me._contextMenuPlaylist.Length > 0 Then
      Me.ContextMenuLibraryPlaylistItemIsFavorite.Enabled = True
      Me.ContextMenuLibraryPlaylistItemIsFavorite.Checked = Me._db.IsPlaylistFavorite(Me._contextMenuPlaylist)
    Else
      Me.ContextMenuLibraryPlaylistItemIsFavorite.Enabled = False
      Me.ContextMenuLibraryPlaylistItemIsFavorite.Checked = False
    End If
  End Sub

  Private Sub ContextMenuItemLibraryPlaylistIsFavorite_Click(ByVal sender As Object, ByVal e As EventArgs) Handles ContextMenuLibraryPlaylistItemIsFavorite.Click
    If Me._contextMenuPlaylist.Length > 0 Then
      ' Flip the favorite flag
      Me._db.IsPlaylistFavorite(Me._contextMenuPlaylist) = Not Me.ContextMenuLibraryPlaylistItemIsFavorite.Checked
      Me._contextMenuPlaylist = String.Empty

      ' Refresh the library menu builder because if we are in the favorites list, this entry might change
      Me._lmb.RefreshMenu()
      Me.PrettyListMiddle.Items = Me._lmb.Items
    End If
  End Sub




  Private Sub ContextMenuEditorItem_Popup(ByVal sender As Object, ByVal e As EventArgs) Handles ContextMenuEditorItem.Popup
    If Me._contextMenuAVPLE IsNot Nothing Then
      ' Set the enabled states for if we can move items up or down in the list
      If Me._editorPlaylist.Entries.Count > 1 Then
        Dim idx As Integer = Me._editorPlaylist.Entries.IndexOf(Me._contextMenuAVPLE)
        If idx >= 1 Then
          Me.ContextMenuItemEditorMoveUp.Enabled = True
        Else
          Me.ContextMenuItemEditorMoveUp.Enabled = False
        End If
        If idx >= 0 AndAlso idx < Me._editorPlaylist.Entries.Count - 1 Then
          Me.ContextMenuItemEditorMoveDown.Enabled = True
        Else
          Me.ContextMenuItemEditorMoveDown.Enabled = False
        End If
      Else
        Me.ContextMenuItemEditorMoveUp.Enabled = False
        Me.ContextMenuItemEditorMoveDown.Enabled = False
      End If

      Me.ContextMenuItemEditorRemoveItem.Enabled = True
    Else
      Me.ContextMenuItemEditorMoveUp.Enabled = False
      Me.ContextMenuItemEditorMoveDown.Enabled = False
      Me.ContextMenuItemEditorRemoveItem.Enabled = False
    End If
  End Sub

  Private Sub ContextMenuItemEditorRemoveItem_Click(ByVal sender As Object, ByVal e As EventArgs) Handles ContextMenuItemEditorRemoveItem.Click
    If Me._contextMenuAVPLE IsNot Nothing Then
      Me._editorPlaylist.Entries.Remove(Me._contextMenuAVPLE)
      Me.PrettyListMiddle.Items = Me._editorPlaylist
      Me.SaveNPCacheFiles(CacheSaveItems.Editor)
      Me._contextMenuAVPLE = Nothing
    End If
  End Sub

  Private Sub ContextMenuItemEditorMoveUp_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ContextMenuItemEditorMoveUp.Click
    If Me._contextMenuAVPLE IsNot Nothing Then
      Dim idx As Integer = Me._editorPlaylist.Entries.IndexOf(Me._contextMenuAVPLE)
      If idx >= 1 Then
        Me._editorPlaylist.Entries.RemoveAt(idx)
        Me._editorPlaylist.Entries.Insert(idx - 1, Me._contextMenuAVPLE)
        Me.PrettyListMiddle.Items = Me._editorPlaylist
        Me.SaveNPCacheFiles(CacheSaveItems.Editor)
      End If
      Me._contextMenuAVPLE = Nothing
    End If
  End Sub

  Private Sub ContextMenuItemEditorMoveDown_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ContextMenuItemEditorMoveDown.Click
    If Me._contextMenuAVPLE IsNot Nothing Then
      Dim idx As Integer = Me._editorPlaylist.Entries.IndexOf(Me._contextMenuAVPLE)
      If idx >= 0 AndAlso idx < Me._editorPlaylist.Entries.Count - 1 Then
        Me._editorPlaylist.Entries.RemoveAt(idx)
        Me._editorPlaylist.Entries.Insert(idx + 1, Me._contextMenuAVPLE)
        Me.PrettyListMiddle.Items = Me._editorPlaylist
        Me.SaveNPCacheFiles(CacheSaveItems.Editor)
      End If
      Me._contextMenuAVPLE = Nothing
    End If
  End Sub




  Private Sub ContextMenuNowPlayingItem_Popup(ByVal sender As Object, ByVal e As EventArgs) Handles ContextMenuNowPlayingItem.Popup
    If Me._contextMenuAVPLE IsNot Nothing Then
      ' Set the enabled states for if we can move items up or down in the list
      If Me.ActiveNowPlayingList.Entries.Count > 1 Then
        Dim idx As Integer = Me.ActiveNowPlayingList.Entries.IndexOf(Me._contextMenuAVPLE)
        If idx >= 1 Then
          Me.ContextMenuItemNowPlayingMoveUp.Enabled = True
        Else
          Me.ContextMenuItemNowPlayingMoveUp.Enabled = False
        End If
        If idx >= 0 AndAlso idx < Me.ActiveNowPlayingList.Entries.Count - 1 Then
          Me.ContextMenuItemNowPlayingMoveDown.Enabled = True
        Else
          Me.ContextMenuItemNowPlayingMoveDown.Enabled = False
        End If
      Else
        Me.ContextMenuItemNowPlayingMoveUp.Enabled = False
        Me.ContextMenuItemNowPlayingMoveDown.Enabled = False
      End If

      ' If this is the currently loaded item, we cannot delete it
      If Playlists.AVPLPlaylistEntry.CurrentPlayingItem Is Me._contextMenuAVPLE Then
        Me.ContextMenuItemNowPlayingEditItem.Enabled = False
        Me.ContextMenuItemNowPlayingRemoveItem.Enabled = False
      Else
        Me.ContextMenuItemNowPlayingEditItem.Enabled = True
        Me.ContextMenuItemNowPlayingRemoveItem.Enabled = True
      End If

      ' Set the context menu stream refresh, based on if the play list item is capable of a stream refresh
      If Me._contextMenuAVPLE.IsStream AndAlso Me._contextMenuAVPLE.StreamPlaylistUrlString.Length > 0 Then
        Me.ContextMenuItemNowPlayingRefreshStreamURLs.Enabled = True
      Else
        Me.ContextMenuItemNowPlayingRefreshStreamURLs.Enabled = False
      End If
    Else
      Me.ContextMenuItemNowPlayingMoveUp.Enabled = False
      Me.ContextMenuItemNowPlayingMoveDown.Enabled = False
      Me.ContextMenuItemNowPlayingRemoveItem.Enabled = False
      Me.ContextMenuItemNowPlayingRefreshStreamURLs.Enabled = False
    End If
  End Sub

  Private Sub ContextMenuItemNowPlayingEditItem_Click(ByVal sender As Object, ByVal e As EventArgs) Handles ContextMenuItemNowPlayingEditItem.Click
    If Me._contextMenuAVPLE IsNot Nothing Then
      Dim avpl As Playlists.AVPLPlaylist = Me.ActiveNowPlayingList
      Dim idx As Integer = avpl.Entries.IndexOf(Me._contextMenuAVPLE)
      If idx >= 0 Then
        Dim pee As New PlaylistEntryEditor()
        pee.Database = Me._db
        pee.PlaylistEntry = Me._contextMenuAVPLE
        pee.SetColors(Skin.FormForegroundColor, Skin.FormBackgroundColor, Skin.FormBackgroundColorTextBox)
        If pee.ShowDialog = Windows.Forms.DialogResult.OK AndAlso avpl Is Me.ActiveNowPlayingList Then
          avpl.Entries(idx).CopyInto(pee.PlaylistEntry)
          Me.PrettyListMiddle.Items = avpl
          Me.SaveNPCacheFiles(avpl)
        End If
      End If
    End If
  End Sub

  Private Sub ContextMenuItemNowPlayingRemoveItem_Click(ByVal sender As Object, ByVal e As EventArgs) Handles ContextMenuItemNowPlayingRemoveItem.Click
    If Me._contextMenuAVPLE IsNot Nothing Then
      Me.RemoveItemFromNowPlayingList(Me._contextMenuAVPLE)
      Me._contextMenuAVPLE = Nothing
    End If
  End Sub

  Private Sub ContextMenuItemNowPlayingRefreshStreamURLs_Click(ByVal sender As Object, ByVal e As EventArgs) Handles ContextMenuItemNowPlayingRefreshStreamURLs.Click
    If Me._contextMenuAVPLE IsNot Nothing Then
      If Me._contextMenuAVPLE.IsStream AndAlso Me._contextMenuAVPLE.StreamPlaylistUrlString.Length > 0 Then
        Try
          Windows.Forms.Cursor.Current = Cursors.WaitCursor
          Me._contextMenuAVPLE.RefreshStreamURLsFromPlaylistURL()
        Catch ex As Threading.ThreadAbortException
          ' Ignore
        Catch ex As Exception
          ErrorLog.WriteToErrorLog("Manual stream refresh failure: " & ex.ToString)
          MessageBox.Show(GetLanguageString("UnableToRefreshStreamURLs"))
        Finally
          Windows.Forms.Cursor.Current = Cursors.Default
        End Try
      End If
      Me._contextMenuAVPLE = Nothing
    End If
  End Sub

  Private Sub ContextMenuItemNowPlayingMoveUp_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ContextMenuItemNowPlayingMoveUp.Click
    If Me._contextMenuAVPLE IsNot Nothing Then
      Dim idx As Integer = Me.ActiveNowPlayingList.Entries.IndexOf(Me._contextMenuAVPLE)
      If idx >= 1 Then
        Dim curList As AVPLPlaylist = Me.ActiveNowPlayingList
        curList.Entries.RemoveAt(idx)
        curList.Entries.Insert(idx - 1, Me._contextMenuAVPLE)
        Me.PrettyListMiddle.Items = curList
        Me.SaveNPCacheFiles(curList)
      End If
      Me._contextMenuAVPLE = Nothing
    End If
  End Sub

  Private Sub ContextMenuItemNowPlayingMoveDown_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ContextMenuItemNowPlayingMoveDown.Click
    If Me._contextMenuAVPLE IsNot Nothing Then
      Dim idx As Integer = Me.ActiveNowPlayingList.Entries.IndexOf(Me._contextMenuAVPLE)
      If idx >= 0 AndAlso idx < Me.ActiveNowPlayingList.Entries.Count - 1 Then
        Dim curList As AVPLPlaylist = Me.ActiveNowPlayingList
        curList.Entries.RemoveAt(idx)
        curList.Entries.Insert(idx + 1, Me._contextMenuAVPLE)
        Me.PrettyListMiddle.Items = curList
        Me.SaveNPCacheFiles(curList)
      End If
      Me._contextMenuAVPLE = Nothing
    End If
  End Sub
#End Region

  Public Sub New()
    ' Load the language file
    If String.Compare(Settings.LanguageFolder, Settings.LanguageFolderDefault, True) = 0 Then
      LoadLanguage(Settings.LanguageFolder, "") ' Do not load default language, because we are using the defalut language
    Else
      LoadLanguage(Settings.LanguageFolder, Settings.LanguageFolderDefault)
    End If

    ' Initialize the designer components
    Me.InitializeComponent()

    ' Set the DPI factor
    Me._dpiFactor = Me.AutoScaleDimensions.Height / 192.0F

    ' Set the key preview property of the form
    Me.KeyPreview = True

    ' Instantiate components that may rely on language files and can't be instantiated in their declaration above
    Me._libraryMonitor = New Library.Monitor(Me._dpiFactor)

    Try
      Me._db = New AvianPlayDB
    Catch ex As TerminateWithoutReportException
      Throw
    Catch ex As Exception
      WriteToErrorLog("!!! DATABASE INSTANTIATION EXCEPTION !!!" & ex.ToString)
      MessageBox.Show(ex.Message, "Avian Play", MessageBoxButtons.OK, MessageBoxIcon.Hand, MessageBoxDefaultButton.Button1)
      Throw New TerminateWithoutReportException  ' We already reported to the user and wrote to the exception log, so just terminate!
    End Try

    Me._lmb = New Library.MenuBuilder(Me._db)
  End Sub

  Public Sub SetAudioEngine(ByVal ae As AudioEngine)
    Me._AudioEngine = ae
  End Sub

  Public Sub SetPodcastManager(ByVal pcm As PodcastManager)
    Me._pcm = pcm
  End Sub

  Private Sub AvianPlayMain_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Disposed
    Me._dsm.StopPreventDeviceSleep()
  End Sub

  Private Sub Form_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
    ' Show wait cursor and disable the form against user input
    Windows.Forms.Cursor.Current = Cursors.WaitCursor
    Me.Enabled = False

    ' Initialize audio engine buffer sizes
    Me._AudioEngine.SetPrebufferPercentage(Settings.PrebufferFill)
    Me._AudioEngine.SetBufferLength(Settings.BufferLength)

    ' Set the top label parameters
    Me.PlaybackLabelTop.SetDefaultControlHeight()
    Me.PlaybackLabelTop.BackgroundBitmap = SkinBitmapTopLabel_Background
    Me.PlaybackLabelTop.StatusBarTopBitmap = SkinBitmapStatusBar_Top
    Me.PlaybackLabelTop.StatusBarBottomBitmap = SkinBitmapStatusBar_Bottom
    Me.PlaybackLabelTop.StatusBarHighlightTopBitmap = SkinBitmapStatusBarHighlight_Top
    Me.PlaybackLabelTop.StatusBarHighlightBottomBitmap = SkinBitmapStatusBarHighlight_Bottom
    Me.PlaybackLabelTop.MemorizedIconBitmap = SkinBitmapTopLabel_MemorizedIcon
    Me.PlaybackLabelTop.ForeColor = Skin.TrackInfoMainTextColor
    Me.PlaybackLabelTop.OverlayForeColor = Skin.TrackInfoOverlayTextColor
    Me.PlaybackLabelTop.HugeOverlayForeColor = Skin.TrackInfoLargeTimerTextColor
    Me.PlaybackLabelTop.BottomStatusStripForeColor = Skin.TrackInfoStatusStripTextColor
    Me.PlaybackLabelTop.LongPressIndicatorColor = Skin.TrackInfoLongPressIndicatorColor

    ' Refresh the playback state to get initial status set
    Me.RefreshPlaybackStates()

    ' Set the button colors
    Me.ControlContainerMain.BackColor = Skin.ControlsBackgroundColor
    Me.ControlContainerMain.ForeColor = Skin.ControlsSectionSplashTextColor
    ControlButton.OverlayTextColor = Skin.ControlsOverlayTextColor
    ControlButton.OutlineColor = Skin.ListKeypadNavigationFrame ' TODO: this should probably have its own color

    ' Set the Album Art display mode in lists
    Playlists.AVPLPlaylistEntry.AlbumArtListDisplayMode = Settings.AlbumArtListDisplayMode

    ' Load buttons and skin elements for the bottom strip and associate Surface
    Me._buttonStripItems.Add(New BottomStripButton(GetLanguageString("BottomMenuMusic"), SkinBitmapBottomRow_Music, SkinBitmapBottomRowHL_Music, Me._musicIndex))   ' Blue Music File Player
    Me._buttonStripItems.Add(New BottomStripButton(GetLanguageString("BottomMenuPodcast"), SkinBitmapBottomRow_Podcast, SkinBitmapBottomRowHL_Podcast, Me._podcastIndex))  ' Green Podcast Player 
    Me._buttonStripItems.Add(New BottomStripButton(GetLanguageString("BottomMenuRadio"), SkinBitmapBottomRow_Radio, SkinBitmapBottomRowHL_Radio, Me._radioIndex))   ' Red Internet Radio Player
    Me._buttonStripItems.Add(New BottomStripButton(GetLanguageString("BottomMenuLibrary"), SkinBitmapBottomRow_DB, SkinBitmapBottomRowHL_DB, Me._libraryIndex))  ' Purple Media file database 
    Me._buttonStripItems.Add(New BottomStripButton(GetLanguageString("BottomMenuEditor"), SkinBitmapBottomRow_Playlist, SkinBitmapBottomRowHL_Playlist, Me._editorIndex))  ' Yellow Playlist library / editor 
    Me._buttonStripItems.Add(New BottomStripButton(GetLanguageString("BottomMenuSettings"), SkinBitmapBottomRow_Settings, SkinBitmapBottomRowHL_Settings, Me._settingsIndex))  ' Orange Settings

    ' Set the visibility of the playback tabs
    Me.SetVisibleTabs()

    ' Wire up the Surface switcher for the bottom strip buttons
    For Each bsb As BottomStripButton In Me._buttonStripItems
      AddHandler bsb.SwitchToViewIndex, AddressOf SwitchToViewIndexHandler
    Next

    ' Set the bottom sliding strip button width from the image width (this value is always at 192dpi) and copy the set the enumerable list into the control
    Me.SlidingButtonStripMain.ButtonWidth = Me._buttonStripItems(0).MainBitmap.Width
    Me.SlidingButtonStripMain.Items = Me._buttonStripItems

    ' Load the bottom strip's pop-up icons
    Dim floatingBmps As New List(Of Bitmap)
    floatingBmps.Add(SkinBitmapFloatingIcon_Music)
    floatingBmps.Add(SkinBitmapFloatingIcon_Podcast)
    floatingBmps.Add(SkinBitmapFloatingIcon_Radio)
    floatingBmps.Add(SkinBitmapFloatingIcon_DB)
    floatingBmps.Add(SkinBitmapFloatingIcon_Playlist)
    floatingBmps.Add(SkinBitmapFloatingIcon_Settings)
    Me.ControlContainerMain.FloatingMessageIcons = floatingBmps

    ' Update bottom menu
    Me.UpdateBottomMenu()

    ' Set colors and other settings on PrettyList
    Me.PrettyListMiddle.BackColor = Skin.ListBackgroundColorEmpty
    Me.PrettyListMiddle.ForeColor = Skin.ListTextColorPrimary
    Me.PrettyListMiddle.ScrollBarBackColor = Skin.ListScrollBarBackgroundColor
    Me.PrettyListMiddle.ScrollBarForeColor = Skin.ListScrollBarForegroundColor
    Me.PrettyListMiddle.ScrollBarHighlightBackColor = Skin.ListScrollBarBackgroundHighlightColor
    Me.PrettyListMiddle.ScrollBarHighlightForeColor = Skin.ListScrollBarForegroundHighlightColor
    Dim sic As MobilePrettyGui.PrettyList.ListColors
    sic.BackgroundColor = Skin.ListBackgroundColor
    sic.BackgroundColorAlternate = Skin.ListBackgroundColorAlternate
    sic.BackgroundColorSelected = Skin.ListBackgroundColorSelected
    sic.TextColor1 = Skin.ListTextColorPrimary
    sic.TextColor2 = Skin.ListTextColorSecondary
    sic.TextColor3 = Skin.ListTextColorHighlight
    sic.TextColor4 = Skin.ListTextColorActive
    sic.TextcolorSelected = Skin.ListTextColorSelected
    sic.NavigationFrameColor = Skin.ListKeypadNavigationFrame
    Me.PrettyListMiddle.SuggestedItemColors = sic
    Me.PrettyListMiddle.UseFixedItemHeight = True
    Me.PrettyListMiddle.ListIsEmptyString = GetLanguageString("NoItemsInList")

    ' Set up breadcrumb defaults
    Me._libraryBreadcrumb.Clear()
    Me._libraryBreadcrumb.Push(GetLanguageString("LibraryBreadcrumbTitle"))
    Me._settingsBreadcrumb.Clear()
    Me._settingsBreadcrumb.Push(GetLanguageString("SettingsBreadcrumbTitle"))

    ' Add handlers for menu builder
    AddHandler Me._lmb.CallAsyncPluginAssignItemsOnMainThread, AddressOf lmb_CallAsyncPluginAssignItemsOnMainThread
    AddHandler Me._lmb.SetListEnabled, AddressOf lmb_SetListEnabled

    ' Start the afterload timer
    Me.TimerAfterLoad.Enabled = True
  End Sub

  Private Sub TimerAfterLoad_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TimerAfterLoad.Tick
    Me.TimerAfterLoad.Enabled = False
    Windows.Forms.Cursor.Current = Cursors.WaitCursor

    ' Initialize the device sleep manager
    If Settings.SystemIdleTimerResetWhilePlaying Then
      Me._dsm.StartPreventDeviceSleep("AvianPlay_Main_")
      Me._dsm.PausePreventDeviceSleep = True
    End If

    ' Instantiate event manager
    Me._em = New EventManager

    ' Check the Library.ini version
    Dim doLibraryUpdate As Boolean = False
    Dim doLibraryResetVersion As Boolean = False

    If Not Me._lmb.IsLibraryVersionValid Then
      If MessageBox.Show(GetLanguageString("LibraryInvalidVersion"), "Avian Play", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) = Windows.Forms.DialogResult.Yes Then
        doLibraryUpdate = True
      Else
        doLibraryResetVersion = True
      End If
    ElseIf Me._lmb.SuggestLibraryUpgrade Then
      If MessageBox.Show(GetLanguageString("LibraryNewerVersion"), "Avian Play", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) = Windows.Forms.DialogResult.Yes Then
        doLibraryUpdate = True
      Else
        doLibraryResetVersion = True
      End If
    End If

    If doLibraryUpdate Then
      Try
        Me._lmb.ResetLibraryINIFile()
        MessageBox.Show(GetLanguageString("LibraryRestoreComplete"), "Avian Play", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1)
      Catch ex As Exception
        WriteToErrorLog("Reset Library INI failed :: " & ex.ToString)
        MessageBox.Show(GetLanguageString("LibraryRestoreFailed"), "Avian Play", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1)
      End Try
      Me.Close()
    ElseIf doLibraryResetVersion Then
      Try
        Me._lmb.ResetLibraryVersion()
      Catch ex As Exception
        WriteToErrorLog("Reset Library version failed :: " & ex.ToString)
        MessageBox.Show(GetLanguageString("LibraryResetfailed"), "Avian Play", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1)
      End Try
    End If

    ' Instantiate the settings menu builder (this may be a lengthy operation, so we want to do it after the app has appeared to the user)
    Me._smb = New SettingsMenuBuilder

    ' Change the toplabel size, depending on the screen size
    With Windows.Forms.Screen.PrimaryScreen.Bounds
      Dim h As Integer = .Height
      If .Width > h Then h = .Width
      h /= Me._dpiFactor
      If h > 640 Then ' "tall" screen (probably WVGA or WQVGA)
        Me.PlaybackLabelTop.Height += 40 * Me._dpiFactor
        Me.PlaybackLabelTop.SetDefaultControlHeight(1)
      ElseIf h < 640 Then ' "short" or "square" screen (probably 320x320 or 240x240)
        Me.PlaybackLabelTop.Height -= 40 * Me._dpiFactor
        Me.PlaybackLabelTop.SetDefaultControlHeight(-1)
      End If
    End With

    ' Fetch the sliding control strip default height
    Me._slidingStripHeight = Me.SlidingButtonStripMain.Height

    ' Build the menu items
    Me.RebuildMenuItems()

    ' Set default open tabs
    Me._currentIndex = Me.GetMusicTabIndex()
    Me._playingIndex = Me.GetMusicTabIndex()
    Me.UpdateBottomMenu()

    ' Bring the control panel to the front
    Me.ControlContainerMain.Visible = True
    Me.ControlContainerMain.Enabled = True
    Me.ControlContainerMain.BringToFront()

    ' Read saved state playlists to represent the Now Playlist lists
    If IO.File.Exists(Settings.NowPlayingMusicPath) Then
      Try
        Me._musicPlaylist = New AVPLPlaylist
        Me._musicPlaylist.LoadFromFile(Settings.NowPlayingMusicPath, True)
      Catch ex As Exception
        WriteToErrorLog("TimerAfterLoad_Tick :: Error while loading NowPlayingMusic :: " & ex.ToString)
      End Try
    End If
    If IO.File.Exists(Settings.NowPlayingPodcastPath) Then
      Try
        Me._podcastPlaylist = New AVPLPlaylist
        Me._podcastPlaylist.LoadFromFile(Settings.NowPlayingPodcastPath, True)
      Catch ex As Exception
        WriteToErrorLog("TimerAfterLoad_Tick :: Error while loading NowPlayingPodcast :: " & ex.ToString)
      End Try
    End If
    If IO.File.Exists(Settings.NowPlayingRadioPath) Then
      Try
        Me._radioPlaylist = New AVPLPlaylist
        Me._radioPlaylist.LoadFromFile(Settings.NowPlayingRadioPath, True)
        Me._radioPlaylist.RefreshStreamURLs(Me, Settings.RefreshStreamURLsFromPLSAfter)
      Catch ex As Exception
        WriteToErrorLog("TimerAfterLoad_Tick :: Error while loading NowPlayingRadio :: " & ex.ToString)
      End Try
    End If
    If IO.File.Exists(Settings.EditorPath) Then
      Try
        Me._editorPlaylist = New AVPLPlaylist
        Me._editorPlaylist.LoadFromFile(Settings.EditorPath)
      Catch ex As Exception
        WriteToErrorLog("TimerAfterLoad_Tick :: Error while loading Editor :: " & ex.ToString)
      End Try
    End If

    ' Start library scan thread
    If Settings.MonitorScannedPathsRealtime Then Me._libraryMonitor.StartRealtimeMonitor()
    If Settings.PerformFullScanOnStartup Then Me._libraryMonitor.RescanMonitoredPaths()

    ' Set the finished loading flag to TRUE so we can restore state
    Me._isFinishedLoading = True

    ' Set status messages
    Me.PlaybackLabelTop.StatusMessage = GetLanguageString("Stopped")
    Me.PlaybackLabelTop.IsReady = True

    ' Load state
    With New Ini.File(Settings.INIStatePath)
      Dim state As Ini.Section = .Section("State")
      If state IsNot Nothing Then
        With state
          ' Note: this needs to be first because LoadPLE will change the control surface to the normal one, which might be different from the LasTab parameter
          If .IntegerValueNoNull("LoadedTab", -1) >= 0 Then
            Me._playingIndex = Me.GetNowPlayingIndexByIndex(.IntegerValueNoNull("LoadedTab"))
            Me._currentPlaylist = Me.GetNowPlayingPlaylistByIndex(Me._playingIndex)

            ' Load last volume
            Dim vol As Byte = 0
            Dim volint As Integer = .IntegerValueNoNull("Volume", 100)
            If volint > 100 Then volint = 100
            If volint < 0 Then volint = 0
            vol = CByte(volint)
            Me.Volume = vol

            ' Load mute state
            Me.Mute = .BooleanValueNoNull("Mute", False)

            ' Load the item into PLE
            Dim itemToLoad As Integer = .IntegerValueNoNull("LoadedItem", -1)
            If itemToLoad >= 0 AndAlso itemToLoad < Me._currentPlaylist.Entries.Count Then
              Me.LoadPLEIntoAudioEngine(Me._currentPlaylist.Entries(itemToLoad))
            End If

            ' Reshuffle
            Me.ReshuffleCurrentPlaylist()

            ' Set the time offset
            Me._AudioEngine.Seek(.IntegerValueNoNull("TimeOffset", 0))
          End If

          ' Set the previous top label view mode
          Me._lastSmallLabel = .BooleanValueNoNull("TopLabelSmall")

          ' Set the repeat mode
          Me._menuItemControlRepeat.Checked = .BooleanValueNoNull("Repeat")
          Me._menuItemControlShuffle.Checked = .BooleanValueNoNull("Shuffle")

          ' Set the previous tab the user was on
          Dim lastTab As Integer = .IntegerValueNoNull("LastTab", -1)
          If lastTab >= 0 Then
            Me.SetSlidingButtonStripIndex(lastTab, False)
            If lastTab = Me._libraryIndex OrElse lastTab = Me._settingsIndex OrElse lastTab = Me._editorIndex Then
              Me.PrettyListMiddleShow()
            Else
              Me.PrettyListMiddleHide()
            End If
          End If
        End With
      End If
    End With

    ' Start the podcast manager
    If Settings.PodcastSubscriptionInterval > 0 Then
      Me._pcm.IntervalHours = Settings.PodcastSubscriptionInterval
      Me._pcm.Start()
    End If

    ' The form can now be used with no lags!
    Windows.Forms.Cursor.Current = Cursors.Default
    Me.PrettyListMiddle.Enabled = True
    Me.Enabled = True

    ' Evaluate the initial SIP state
    Me.InputPanelMain_EnabledChanged(sender, EventArgs.Empty)

    ' We need to refresh the playback button states, now that we are done restoring state.  The _lastNoItemLoaded setting is a hack to get the refresh to go (it normally wouldn't fire due to the 'last' status equaling the current status)
    Me._lastNoItemLoaded = Not Me._noItemLoaded
    Me.RefreshPlaybackStates()

    ' Call the event manager OnApplicationStart event (this should be the LAST thing done on startup)
    Me._em.OnApplicationStart()

    ' Okay... Now show the welcome message!
    Me.ShowOverlayTimedMessage(GetLanguageString("Welcome"))
  End Sub

  Public ReadOnly Property GracefulExit() As Boolean
    Get
      Return Me._gracefulExit
    End Get
  End Property

  Private Sub Form_Closing(ByVal sender As Object, ByVal e As ComponentModel.CancelEventArgs) Handles Me.Closing
    Static IsCloseStarted As Boolean = False
    Dim timeOffset As Integer = 0

    If Not IsCloseStarted Then
      IsCloseStarted = True
      Me.Enabled = False

      ' Call the OnApplicationExit event (this should be the FIRST thing we do on exit)
      If Me._isFinishedLoading Then Me._em.OnApplicationExit()

      ' Set the flag that we are exiting (or starting to exit)
      Me._isExiting = True

      ' Stop all timers
      Me.TimerAfterLoad.Enabled = False
      Me.TimerControlsButtonLongPress.Enabled = False
      Me.TimerFwdRewind.Enabled = False
      Me.TimerOverlay.Enabled = False
      Me.TimerPlayAfterBriefDelay.Enabled = False
      Me.TimerStartFullScan.Enabled = False
      Me.TimerStreamRetry.Enabled = False
      Me.TimerStatusMessageFlip.Enabled = False
      Me.TimerTrackCounter.Enabled = False
      Me.TimerUpdateTrackPositionInDB.Enabled = False

      ' Stop playback gracefully!
      If Not Me._AudioEngine.IsStream Then timeOffset = Me.PlaybackLabelTop.TrackTime ' Store the timeoffset for later use
      Me.ProcessButtonPress(ControlFunctions.Functions.Stop)

      ' Hide the form
      Me.SendToBack()
      Me.Hide()

      ' Wait for Cache Save Thread to finish
      If Me._cacheSaveThread IsNot Nothing Then
        Me._cacheSaveQuit = True
        Me._cacheSaveThread.Join(2500)
        If Me._cacheSaveThread IsNot Nothing Then
          Try : Me._cacheSaveThread.Abort() : Catch : End Try
        End If
      End If

      ' Write temporary playlists to represent the Now Playlist lists (NPCache)
      If Settings.SaveNPCache Then
        Me._musicPlaylist.SaveToFile(Settings.NowPlayingMusicPath)
        Me._podcastPlaylist.SaveToFile(Settings.NowPlayingPodcastPath)
        Me._radioPlaylist.SaveToFile(Settings.NowPlayingRadioPath)
        Me._editorPlaylist.SaveToFile(Settings.EditorPath)
      End If

      ' Kill the podcast manager
      Me._pcm.RaiseEvents = False
      Me._pcm.Stop()

      ' Remove LMB handlers
      RemoveHandler Me._lmb.CallAsyncPluginAssignItemsOnMainThread, AddressOf lmb_CallAsyncPluginAssignItemsOnMainThread
      RemoveHandler Me._lmb.SetListEnabled, AddressOf lmb_SetListEnabled

      ' Save state
      With New Ini.File
        .AddSection("State")

        With .Section("State")
          .Add("LastTab", Me._currentIndex)
          .Add("LoadedTab", Me._playingIndex)
          .Add("TopLabelSmall", Me._lastSmallLabel)
          .Add("TimeOffset", timeOffset)
          .Add("Repeat", Me._menuItemControlRepeat.Checked)
          .Add("Shuffle", Me._menuItemControlShuffle.Checked)
          .Add("Volume", Me.Volume)
          .Add("Mute", Me.Mute)

          Dim curPL As AVPLPlaylist = Nothing
          Select Case Me._playingIndex
            Case Me._musicIndex
              curPL = Me._musicPlaylist
            Case Me._podcastIndex
              curPL = Me._podcastPlaylist
            Case Me._radioIndex
              curPL = Me._radioPlaylist
          End Select

          Dim i = -1
          If curPL IsNot Nothing Then
            If curPL.Entries.Count > 0 Then
              Dim found As Boolean = False
              Do
                i += 1
                If curPL.Entries(i) Is AVPLPlaylistEntry.CurrentPlayingItem Then
                  found = True
                  Exit Do
                End If
              Loop While i < curPL.Entries.Count - 1
              If Not found Then
                i = -1
              End If
            End If
          End If
          .Add("LoadedItem", i.ToString)
        End With

        .Save(Settings.INIStatePath)
      End With

      ' Stop the device sleep manager (also stopped in the dispose overload, just in case)
      Me._dsm.StopPreventDeviceSleep()

      ' Now we can begin shutting down the application
      Me._gracefulExit = True
      Me._AudioEngine.GracefulExit = True ' the audio engine won't throw errors or restart the audio engine if this flag is true

      For Each bsb As BottomStripButton In Me._buttonStripItems
        RemoveHandler bsb.SwitchToViewIndex, AddressOf SwitchToViewIndexHandler
      Next

      If Me._lmb IsNot Nothing Then
        RemoveHandler Me._lmb.CallAsyncPluginAssignItemsOnMainThread, AddressOf lmb_CallAsyncPluginAssignItemsOnMainThread
        RemoveHandler Me._lmb.SetListEnabled, AddressOf lmb_SetListEnabled
      End If

      If Me._libraryMonitor IsNot Nothing AndAlso Me._libraryMonitor.IsAnyScanThreadActive Then
        Me._libraryMonitor.StopAllMonitors()

        ' Block until the library monitors are done or ~60 seconds has elapsed to avoid SQL CE exceptions
        If Me._libraryMonitor.IsAnyScanThreadActive Then
          e.Cancel = True
          Me.TimerClosing.Enabled = True
        End If
      End If
    End If
  End Sub

  Private Sub TimerClosing_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TimerClosing.Tick
    Static count As Integer = 0

    If Not Me._libraryMonitor.IsAnyScanThreadActive OrElse count >= 240 Then ' ~60 seconds since the interval is 250ms
      Me.Close()
    End If

    count += 1
  End Sub

  Private Delegate Sub WriteErrorOnMainThreadDelegate(ByVal err As String)
  Private Sub WriteErrorOnMainThread(ByVal err As String) Handles _libraryMonitor.WriteErrorOnMainThread, _pcm.WriteErrorOnMainThread
    If Me.InvokeRequired Then
      Dim del As New WriteErrorOnMainThreadDelegate(AddressOf WriteErrorOnMainThread)
      Me.Invoke(del, New Object() {err})
    Else
      ErrorLog.WriteToErrorLog(err)
    End If
  End Sub

  Private Delegate Sub StartFullScanAfterDelayOnMainThreadDelegate()
  Private Sub LibraryMonitor_StartFullScanAfterDelayOnMainThread() Handles _libraryMonitor.StartFullScanAfterDelayOnMainThread
    If Me.InvokeRequired Then
      Dim del As New StartFullScanAfterDelayOnMainThreadDelegate(AddressOf LibraryMonitor_StartFullScanAfterDelayOnMainThread)
      Me.Invoke(del)
    Else
      Me.TimerStartFullScan.Enabled = True
    End If
  End Sub

  Private Sub TimerStartFullScan_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TimerStartFullScan.Tick
    If Not Me._isExiting Then
      Me.TimerStartFullScan.Enabled = False
      Me._libraryMonitor.RescanMonitoredPaths()
    End If
  End Sub

  Private Delegate Sub MonitorUpdateStatusChangedDelegate(ByVal monitorIsUpdating As Boolean, ByVal realtimeIsUpdating As Boolean)
  Private Sub LibraryMonitor_MonitorUpdateStatusChanged(ByVal monitorIsUpdating As Boolean, ByVal realtimeIsUpdating As Boolean) Handles _libraryMonitor.MonitorUpdateStatusChanged
    If Me.InvokeRequired Then
      Dim del As New MonitorUpdateStatusChangedDelegate(AddressOf LibraryMonitor_MonitorUpdateStatusChanged)
      Me.Invoke(del, New Object() {monitorIsUpdating, realtimeIsUpdating})
    Else
      If Me._lastMonitorIsUpdating <> monitorIsUpdating Then
        Me._lastMonitorIsUpdating = monitorIsUpdating

        If Me._libraryRestartScan AndAlso Not Me._lastMonitorIsUpdating Then
          Me._libraryRestartScan = False
          If Not Me._isExiting Then Me._libraryMonitor.RescanMonitoredPaths()
        End If
      End If

      If Me._currentIndex = Me._libraryIndex Then
        Me.SetMenuLibrary(Me.MenuItemRight.MenuItems)
        Me.PrettyListMiddle.InvalidateSmart()
      End If
    End If
  End Sub

  Private Delegate Sub RealtimeUpdateManagedPlaylistsDelegate(ByVal oldFileName As String, ByVal newFileName As String)
  Private Sub LibraryMonitor_RealtimeUpdateManagedPlaylists(ByVal oldFileName As String, ByVal newFileName As String) Handles _libraryMonitor.RealtimeUpdateManagedPlaylists
    If Me.InvokeRequired Then
      Dim del As New RealtimeUpdateManagedPlaylistsDelegate(AddressOf LibraryMonitor_RealtimeUpdateManagedPlaylists)
      Me.Invoke(del, New Object() {oldFileName, newFileName})
    Else
      Dim toRemove As New List(Of Playlists.AVPLPlaylistEntry)

      ' Look in MUSIC playlist
      For Each ple As Playlists.AVPLPlaylistEntry In Me._musicPlaylist.Entries
        If ple.IsFile AndAlso String.Compare(ple.FilePath, oldFileName, True) = 0 Then
          If newFileName = String.Empty Then ' File is deleted
            toRemove.Add(ple)
          Else                               ' File is moved/renamed
            ple.FilePath = newFileName
          End If
        End If
      Next
      For Each ple As Playlists.AVPLPlaylistEntry In toRemove
        Me._musicPlaylist.Entries.Remove(ple)
      Next
      toRemove.Clear()

      ' Look in RADIO playlist
      For Each ple As Playlists.AVPLPlaylistEntry In Me._radioPlaylist.Entries
        If ple.IsFile AndAlso String.Compare(ple.FilePath, oldFileName, True) = 0 Then
          If newFileName = String.Empty Then ' File is deleted
            toRemove.Add(ple)
          Else                               ' File is moved/renamed
            ple.FilePath = newFileName
          End If
        End If
      Next
      For Each ple As Playlists.AVPLPlaylistEntry In toRemove
        Me._radioPlaylist.Entries.Remove(ple)
      Next
      toRemove.Clear()

      ' Look in PODCAST playlist
      For Each ple As Playlists.AVPLPlaylistEntry In Me._podcastPlaylist.Entries
        If ple.IsFile AndAlso String.Compare(ple.FilePath, oldFileName, True) = 0 Then
          If newFileName = String.Empty Then ' File is deleted
            toRemove.Add(ple)
          Else                               ' File is moved/renamed
            ple.FilePath = newFileName
          End If
        End If
      Next
      For Each ple As Playlists.AVPLPlaylistEntry In toRemove
        Me._podcastPlaylist.Entries.Remove(ple)
      Next
      toRemove.Clear()
    End If
  End Sub


  Private Sub AudioEngine_StatusChanged(ByVal status As AudioEngine.MAP_STATUS, ByVal err As Integer) Handles _AudioEngine.StatusChanged
    Me._lastStatus = status
    Me._AudioEngine.RefreshEngineFileData()
    Me.UpdateStatusMessage()
    Me.RefreshPlaybackStates()
    'Me.UpdateStatusMessage() ' TODO: Do I need this second one?
  End Sub

  Private Sub AudioEngine_StreamStatusChanged(ByVal status As AudioEngine.MAP_STREAMING_STATUS) Handles _AudioEngine.StreamStatusChanged
    Me._lastStreamStatus = status
    Me.UpdateStatusMessage() ' Do this first since stream retry is initiated here
    Me.RefreshPlaybackStates()
  End Sub

  Private Sub AudioEngine_BitrateChanged(ByVal bitrate As Integer) Handles _AudioEngine.BitrateChanged
    Me._statusBitrate = bitrate
    Me.UpdateStatusMessage()
  End Sub

  Private Sub AudioEngine_DurationChanged(ByVal duration As Integer) Handles _AudioEngine.DurationChanged
    Me.PlaybackLabelTop.TrackLength = duration
    ControlButton.TrackSliderMax = duration
    Me.ControlContainerMain.InvalidateSmart()
  End Sub

  Private Sub AudioEngine_Error(ByVal msg As String, ByVal err As String, ByVal retryable As Boolean) Handles _AudioEngine.MADError
    ' Log the error
    ErrorLog.WriteToErrorLog("Audio Engine Error :: " & err)

    ' On the audio engine error, we need to return to a consistent state, which requires resetting whatever was going on in Avian Play
    If Me._AudioEngine.IsStream Then
      ' Stop the retry counter, if it's going
      Me.TimerStreamRetry.Enabled = False

      ' Stop the playback on the audio engine directly (just in case), but don't update the 'last button' so that retries continue
      Me._AudioEngine.Stop(True)

      ' At this point, it might just be stuck in cancelling because playback already stopped on the engine side, but status was not updated, so we need to force the status update here
      Me._lastStreamStatus = MAP_STREAMING_STATUS.DISCONNECTED

      ' Now update the status message (which also does some additional work on getting states settled)
      Me.UpdateStatusMessage()
    Else
      ' Stop the audio engine
      Me._AudioEngine.Stop()

      ' Do a 'button press' stop now.  We do two because the one on the audio engine stops playback no matter what the conditions are.  After it is stopped, there may be some cleanup work that can be done and that is handled in the button press, as if the user pressed stop.
      Me.ProcessButtonPress(ControlFunctions.Functions.Stop)

      ' Show the user an error occurred only on non-streams
      Me.ShowOverlayTimedMessage(msg)
    End If

    If retryable Then
      ' Reopen whatever the last file was (this is a safety thing to make sure the file is still loaded at this point, before any sort of retry happens)
      Me._AudioEngine.ReOpen()
    Else
      ' tell the button processor that we are closing and stopping because this is not recoverable -- this will also reset any states that are hung from the previous operation
      Me.ProcessButtonPress(ControlFunctions.Functions.Stop)
      Me.ProcessButtonPress(ControlFunctions.Functions.Close)

      ' Now show the error dialog with translation
      MessageBox.Show(msg, "Avian Play", MessageBoxButtons.OK, MessageBoxIcon.Hand, MessageBoxDefaultButton.Button1)
    End If

    ' Update playback states based on what we did above
    Me.RefreshPlaybackStates()
  End Sub

  Private Delegate Sub AudioEngineExitAvianPlayDelegate()
  Private Sub AudioEngine_ExitAvianPlay() Handles _AudioEngine.ExitAvianPlay
    If Me.InvokeRequired Then
      Dim dlg As New AudioEngineExitAvianPlayDelegate(AddressOf AudioEngine_ExitAvianPlay)
      Me.Invoke(dlg)
    Else
      Me.Close()
    End If
  End Sub

  Private Sub AudioEngine_TitleDataChanged(ByVal artist As String, ByVal title As String, ByVal realArtist As String, ByVal realTitle As String, ByVal resetTimer As Boolean) Handles _AudioEngine.TitleDataChanged
    Me.UpdateTitle(artist, title, realArtist, realTitle, resetTimer)
  End Sub

  Private Delegate Sub UpdateTitleDelegate(ByVal artist As String, ByVal title As String, ByVal realArtist As String, ByVal realTitle As String, ByVal resetTimer As Boolean)
  Private Sub UpdateTitle(ByVal artist As String, ByVal title As String, ByVal realArtist As String, ByVal realTitle As String, ByVal resetTimer As Boolean)
    If Me.PlaybackLabelTop.InvokeRequired Then
      Dim utd As New UpdateTitleDelegate(AddressOf UpdateTitle)
      Me.PlaybackLabelTop.Invoke(utd, New Object() {artist, title, realArtist, realTitle, resetTimer})
    Else
      If artist Is Nothing OrElse title Is Nothing OrElse realArtist Is Nothing OrElse realTitle Is Nothing Then
        Me._lastArtist = String.Empty ' Resetting the title
        Me._lastTitle = String.Empty ' Resetting the title
      ElseIf Me._lastArtist <> realArtist OrElse Me._lastTitle <> realTitle Then
        If Me._checkMemorizeThread IsNot Nothing Then
          Try : Me._checkMemorizeThread.Abort() : Catch : End Try
        End If
        Me.PlaybackLabelTop.ShowMemorizeIcon = False

        Me.PlaybackLabelTop.OverlayText = ""
        Me.PlaybackLabelTop.Artist = artist
        Me.PlaybackLabelTop.Title = title

        If resetTimer AndAlso (Me._lastArtist <> realArtist OrElse Me._lastTitle <> realTitle) AndAlso (Me._lastArtist <> "" OrElse Me._lastTitle <> "") Then
          Dim running As Boolean = Me._streamSongStopWatch.IsRunning
          Me._streamSongStopWatch.Reset()
          If running Then
            Me._streamSongStopWatch.Start()
          End If
        End If

        Me._lastArtist = realArtist
        Me._lastTitle = realTitle

        If Me._AudioEngine.IsStream Then
          Me._checkMemorizeThread = New Threading.Thread(AddressOf CheckMemorizeThread)
          Me._checkMemorizeThread.Name = "CheckMemorizeThread"
          Me._checkMemorizeThread.Priority = Threading.ThreadPriority.Lowest

          SyncLock MainThreadPool
            MainThreadPool.Add(Me._checkMemorizeThread)
          End SyncLock

          Me._checkMemorizeThread.Start()
        End If
      End If
    End If
  End Sub

  Private Sub PreviousLMBMenu()
    If Me._lmb.PreviousMenu() Then
      Me.PrettyListMiddle.Items = Me._lmb.Items

      If Me._libraryBreadcrumb.Count > 1 Then
        Me._libraryBreadcrumb.Pop()
      End If

      Dim mnu As String = Me._lmb.CurrentMenu

      If Me._libraryScrollPosition.Count > 0 AndAlso Me._libraryScrollPosition.Count = Me._libraryBreadcrumb.Count Then
        Me.PrettyListMiddle.ScrollPosition = Me._libraryScrollPosition.Pop
      Else
        Me.PrettyListMiddle.ScrollPosition = 0
      End If

      If mnu.Length <= 0 OrElse mnu.ToUpper = "MAIN" Then
        Me.MenuItemLeft.Enabled = False

        ' Reset these values, just in case...
        Me._libraryScrollPosition.Clear()
        Me._libraryBreadcrumb.Clear()
        Me._libraryBreadcrumb.Push(GetLanguageString("LibraryBreadcrumbTitle"))
      End If
    End If

    ' Set the breadcrumb display from the queued value
    Me.BreadcrumbLabelMain.SetBreadcrumbTextFromStack(Me._libraryBreadcrumb)
  End Sub

  Private Sub PreviousSMBMenu()
    Me._smb.LoadMenu("") ' Load the root menu (the settings menu is only two levels deep, so back always takes to the root menu)
    Me.PrettyListMiddle.Items = Me._smb.Items
    Me.PrettyListMiddle.ScrollPosition = Me._settingScrollPosition
    Me._settingScrollPosition = 0
    Me._settingsBreadcrumb.Clear()
    Me._settingsBreadcrumb.Push(GetLanguageString("SettingsBreadcrumbTitle"))
    Me.BreadcrumbLabelMain.SetBreadcrumbTextFromStack(Me._settingsBreadcrumb)
    Me.MenuItemLeft.Enabled = False
  End Sub

  Private Sub MenuItemLeft_Click(ByVal sender As Object, ByVal e As EventArgs) Handles MenuItemLeft.Click
    Select Case Me._currentIndex
      Case Me._libraryIndex
        Me.PreviousLMBMenu()

      Case Me._editorIndex
        If Me._editorPlaylist.Count > 0 Then
          Dim po As New PlaylistOrganize
          po.SetColors(Skin.FormBackgroundColor, Skin.FormForegroundColor, FormForegroundInactiveColor)
          po.SetListItemFromAVPL(Me._editorPlaylist)
          If po.ShowDialog() = Windows.Forms.DialogResult.OK Then
            Dim tempEditorList As New List(Of AVPLPlaylistEntry)

            For Each ape As AVPLPlaylistEntry In Me._editorPlaylist
              tempEditorList.Add(ape)
            Next

            Me._editorPlaylist.Entries.Clear()

            For Each hashCode As Integer In po.ListItemHashes
              Dim removeItem As AVPLPlaylistEntry = Nothing
              For Each ape As AVPLPlaylistEntry In tempEditorList
                If ape.GetHashCode = hashCode Then
                  removeItem = ape
                  Me._editorPlaylist.Entries.Add(ape)
                End If
              Next
              If removeItem IsNot Nothing Then
                tempEditorList.Remove(removeItem)
              Else
                ErrorLog.WriteToErrorLog("Playlist Editor : Organize : Saving Changes :: Items are out of sync with editor list")
              End If
            Next

            Me.PrettyListMiddle.ScrollPosition = 0
            Me._editorScrollPositionSlide = 0
            Me.PrettyListMiddle.Items = Me._editorPlaylist
            Me.SaveNPCacheFiles(CacheSaveItems.Editor)
          End If
        Else
          MessageBox.Show(GetLanguageString("EditorListEmpty"), "Avian Play", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1)
        End If

      Case Me._settingsIndex
        Me.PreviousSMBMenu()

      Case Else ' Any of the tabs with 'now playing' lists
        If Me.PrettyListMiddle.Visible Then
          Me.NowPlayingListHide()
        Else
          Me.NowPlayingListShow()
        End If
    End Select
  End Sub

  Private Sub MenuItemRight_Click(ByVal sender As Object, ByVal e As EventArgs) Handles MenuItemRight.Click
    Select Case Me._currentIndex
      Case Me._musicIndex
      Case Me._podcastIndex
      Case Me._radioIndex
      Case Me._libraryIndex
      Case Me._editorIndex
      Case Me._settingsIndex
    End Select
  End Sub

  Private Sub MenuItemLeft_Popup(ByVal sender As Object, ByVal e As EventArgs) Handles MenuItemLeft.Popup
    Select Case Me._currentIndex
      Case Me._musicIndex
      Case Me._podcastIndex
      Case Me._radioIndex
      Case Me._libraryIndex
      Case Me._editorIndex
      Case Me._settingsIndex
    End Select
  End Sub

  Private Sub MenuItemRight_Popup(ByVal sender As Object, ByVal e As EventArgs) Handles MenuItemRight.Popup
    Select Case Me._currentIndex
      Case Me._musicIndex, Me._podcastIndex, Me._radioIndex
        If Not Me.PrettyListMiddle.Visible Then ' This means the control surface is visible
          Me.SetMenuItemSourceSubmenuItems()
          Me._menuItemControlNextScene.Enabled = Me.CanPreviousOrNextScene
        End If
      Case Me._libraryIndex
      Case Me._editorIndex
        If Me._editorPlaylist.Count > 0 Then
          Me._menuItemEditorSaveAs.Enabled = True
          Me._menuItemEditorSendTo.Enabled = True
          Me._menuItemEditorSendToNPMusic.Enabled = Me.IsMusicTabVisible
          Me._menuItemEditorSendToNPPodcast.Enabled = Me.IsPodcastTabVisible
          Me._menuItemEditorSendToNPRadio.Enabled = Me.IsRadioTabVisible
          Me._menuItemEditorSort.Enabled = True
          Me._menuItemEditorSortAlphaL1.Enabled = True
          Me._menuItemEditorSortAlphaL2.Enabled = True
          Me._menuItemEditorSortType.Enabled = True
          Me._menuItemEditorReverse.Enabled = True
          Me._menuItemEditorClearList.Enabled = True
        Else
          Me._menuItemEditorSaveAs.Enabled = False
          Me._menuItemEditorSendTo.Enabled = False
          Me._menuItemEditorSendToNPMusic.Enabled = False
          Me._menuItemEditorSendToNPPodcast.Enabled = False
          Me._menuItemEditorSendToNPRadio.Enabled = False
          Me._menuItemEditorSort.Enabled = False
          Me._menuItemEditorSortAlphaL1.Enabled = False
          Me._menuItemEditorSortAlphaL2.Enabled = False
          Me._menuItemEditorSortType.Enabled = False
          Me._menuItemEditorReverse.Enabled = False
          Me._menuItemEditorClearList.Enabled = False
        End If
        If Me.IsMusicTabVisible AndAlso Me._musicPlaylist.Count > 0 Then
          Me._menuItemEditorOpenNPMusic.Enabled = True
        Else
          Me._menuItemEditorOpenNPMusic.Enabled = False
        End If
        If Me.IsPodcastTabVisible AndAlso Me._podcastPlaylist.Count > 0 Then
          Me._menuItemEditorOpenNPPodcast.Enabled = True
        Else
          Me._menuItemEditorOpenNPPodcast.Enabled = False
        End If
        If Me.IsRadioTabVisible AndAlso Me._radioPlaylist.Count > 0 Then
          Me._menuItemEditorOpenNPRadio.Enabled = True
        Else
          Me._menuItemEditorOpenNPRadio.Enabled = False
        End If
      Case Me._settingsIndex
    End Select
  End Sub

  Private Sub SetMenuItemSourceSubmenuItems()
    For Each mi As MenuItem In Me._menuItemControlSource.MenuItems
      Try : RemoveHandler mi.Click, AddressOf MenuItemControlSourceSubMenu_Click : Catch : End Try
    Next

    Me._menuItemControlSource.MenuItems.Clear()

    If Playlists.AVPLPlaylistEntry.CurrentPlayingItem IsNot Nothing AndAlso Playlists.AVPLPlaylistEntry.CurrentPlayingItem.IsStream AndAlso Playlists.AVPLPlaylistEntry.CurrentPlayingItem.StreamURLs.Count > 0 Then
      For i As Integer = 0 To Playlists.AVPLPlaylistEntry.CurrentPlayingItem.StreamURLs.Count - 1
        Dim mi As New MenuItem
        mi.Text = (i + 1).ToString & ". " & Playlists.AVPLPlaylistEntry.CurrentPlayingItem.StreamURLs(i).ToString
        mi.Enabled = True
        If i = Me._lastStreamURLIndex Then mi.Checked = True
        AddHandler mi.Click, AddressOf MenuItemControlSourceSubMenu_Click
        Me._menuItemControlSource.MenuItems.Add(mi)
      Next
      Me._menuItemControlSource.Enabled = True
    Else
      Me._menuItemControlSource.MenuItems.Add(New MenuItem) ' Just add an empty one to get the expand arrow
      Me._menuItemControlSource.Enabled = False
    End If
  End Sub

  Private Sub SlidingButtonStripMain_SlidingItemChanged() Handles SlidingButtonStripMain.SlidingItemChanged
    If Me.SlidingButtonStripMain.LastSlideItem IsNot Nothing Then
      Me.ControlContainerMain.ShowFloatingMessage(Me._buttonStripItems.IndexOf(Me.SlidingButtonStripMain.LastSlideItem), Me.SlidingButtonStripMain.LastSlideItem.Name)
    Else
      Me.ControlContainerMain.HideFloatingMessage(500)
    End If
  End Sub

  Private Sub SwitchToViewIndexHandler(ByVal index As Integer)
    Me.SetSlidingButtonStripIndex(index, True)
  End Sub

  Private Sub SetSlidingButtonStripIndex(ByVal index As Integer, ByVal showFloatingMessage As Boolean)
    If Me._isFinishedLoading AndAlso index <> Me._currentIndex Then
      ' Store the current scroll position
      Select Case Me._currentIndex
        Case Me._libraryIndex
          Me._libraryScrollPositionSlide = Me.PrettyListMiddle.ScrollPosition
        Case Me._editorIndex
          Me._editorScrollPositionSlide = Me.PrettyListMiddle.ScrollPosition
        Case Me._settingsIndex
          Me._settingScrollPositionSlide = Me.PrettyListMiddle.ScrollPosition
      End Select

      ' Set the new index
      Me._currentIndex = index

      ' activate the floating message, if we are showing one
      If showFloatingMessage Then
        Me.ControlContainerMain.ShowFloatingMessage(index, Me.SlidingButtonStripMain.SelectedItem.Name)

        If Me._currentIndex = Me._musicIndex OrElse Me._currentIndex = Me._podcastIndex OrElse Me._radioIndex Then
          Me.ControlContainerMain.HideFloatingMessage(500, True)
        Else
          Me.ControlContainerMain.HideFloatingMessage(500)
        End If
      End If

      ' Check the bounds of the control scenes, set to -1 if not a control scene
      If Me._currentIndex <> Me.ControlContainerMain.CurrentSurface AndAlso index < Me.ControlContainerMain.Surfaces.Count Then
        Me.ControlContainerMain.CurrentSurface = Me._currentIndex
      Else
        Me.ControlContainerMain.CurrentSurface = -1
      End If

      ' Determine if the top label should be collapsed
      If Not Me.InputPanelMain.Enabled AndAlso Me.ControlContainerMain.CurrentSurface >= 0 Then
        Me.PlaybackLabelTop.SmallMode = Me._lastSmallLabel
      Else
        Me.PlaybackLabelTop.SmallMode = True
      End If

      ' Set the bottom strip's visible location to where we really are (otherwise button may not move when invoked via code instead of dragging your finger)
      If Me.SlidingButtonStripMain.LastSlideItem Is Nothing OrElse Me._currentIndex <> Me.SlidingButtonStripMain.LastSlideItemIndex Then
        Me.SlidingButtonStripMain.SetSelectedItemIndexNoRaise(index)
      End If

      ' Now we activate the new foreground
      Select Case Me._currentIndex
        Case Me._musicIndex
          Me.ControlContainerMain.CurrentSurface = Me._currentIndex
          Me._prettyListMiddleShouldBeVisible = False
          Me.PrettyListMiddleHide()
          Me.PrettyListMiddle.Enabled = True
        Case Me._podcastIndex
          Me.ControlContainerMain.CurrentSurface = Me._currentIndex
          Me._prettyListMiddleShouldBeVisible = False
          Me.PrettyListMiddleHide()
          Me.PrettyListMiddle.Enabled = True
        Case Me._radioIndex
          Me.ControlContainerMain.CurrentSurface = Me._currentIndex
          Me._prettyListMiddleShouldBeVisible = False
          Me.PrettyListMiddleHide()
          Me.PrettyListMiddle.Enabled = True
        Case Me._libraryIndex
          Me.ControlContainerMain.CurrentSurface = -1
          Me._prettyListMiddleShouldBeVisible = True
          Me.PrettyListMiddleHide()
          Me.BreadcrumbLabelMain.SetBreadcrumbTextFromStack(Me._libraryBreadcrumb)
          Me.PrettyListMiddle.Items = Me._lmb.Items
          Me.PrettyListMiddle.ScrollPosition = Me._libraryScrollPositionSlide
          SyncLock Me.PrettyListMiddle ' Synclocked when this is set to make sure we don't set this out of order
            Me.PrettyListMiddle.Enabled = Me._libraryIsEnabled
          End SyncLock
          ' Check to see if we need to refresh the items
          If Settings.RefreshItemListAfter >= 0 AndAlso Me._lmb.ItemsAreAVPLEntries Then
            If Date.Now.Subtract(Me._lmb.LastRefreshTime).TotalMinutes >= Settings.RefreshItemListAfter Then
              Me._lmb.RefreshMenu()
              Me.PrettyListMiddle.Items = Me._lmb.Items
              Me.PrettyListMiddle.ScrollPosition = 0
            End If
          End If
        Case Me._editorIndex
          Me.ControlContainerMain.CurrentSurface = -1
          Me._prettyListMiddleShouldBeVisible = True
          Me.PrettyListMiddleHide()
          Me.PrettyListMiddle.Enabled = True
          Me.PrettyListMiddle.Items = Me._editorPlaylist
          Me.PrettyListMiddle.ScrollPosition = Me._editorScrollPositionSlide
        Case Me._settingsIndex
          Me.ControlContainerMain.CurrentSurface = -1
          Me._prettyListMiddleShouldBeVisible = True
          Me.PrettyListMiddleHide()
          Me.PrettyListMiddle.Enabled = True
          Me.PrettyListMiddle.Items = Me._smb.Items
          Me.PrettyListMiddle.ScrollPosition = Me._settingScrollPositionSlide
          Me.BreadcrumbLabelMain.SetBreadcrumbTextFromStack(Me._settingsBreadcrumb)
      End Select

      Me.InputPanelMain.Enabled = False
      Me.RefreshPlaybackStates()
      Me.UpdateBottomMenu()
    End If
  End Sub

  Private Sub UpdateBottomMenu()
    ' The right soft key is always 'menu'
    Me.MenuItemRight.Enabled = True
    Me.MenuItemRight.Text = GetLanguageString("Menu")

    ' Now set the left soft key and right soft key's menu items (if applicable)
    Select Case Me._currentIndex
      Case Me._musicIndex, Me._podcastIndex, Me._radioIndex
        If Me.PrettyListMiddle.Visible Then
          Me.MenuItemLeft.Enabled = True
          Me.MenuItemRight.Enabled = True
          Me.MenuItemLeft.Text = GetLanguageString("SoftKeyControls")
        Else
          Me.MenuItemLeft.Enabled = True
          Me.MenuItemRight.Enabled = True
          Me.MenuItemLeft.Text = GetLanguageString("SoftKeyNowPlaying")
        End If
        Select Case Me._currentIndex
          Case Me._musicIndex
            If Me.PrettyListMiddle.Visible Then
              Me.SetMenuNowPlaying(Me.MenuItemRight.MenuItems, False)
            Else
              Me.SetMenuMusic(Me.MenuItemRight.MenuItems)
            End If
          Case Me._podcastIndex
            If Me.PrettyListMiddle.Visible Then
              Me.SetMenuNowPlaying(Me.MenuItemRight.MenuItems, False)
            Else
              Me.SetMenuPodcast(Me.MenuItemRight.MenuItems)
            End If
          Case Me._radioIndex
            If Me.PrettyListMiddle.Visible Then
              Me.SetMenuNowPlaying(Me.MenuItemRight.MenuItems, True)
            Else
              Me.SetMenuRadio(Me.MenuItemRight.MenuItems)
            End If
        End Select
      Case Me._libraryIndex
        Me.MenuItemLeft.Enabled = Me._lmb.CanGoPreviousMenu
        Me.MenuItemLeft.Text = GetLanguageString("Back")
        Me.SetMenuLibrary(Me.MenuItemRight.MenuItems)
      Case Me._editorIndex
        Me.MenuItemLeft.Enabled = True
        Me.MenuItemLeft.Text = GetLanguageString("SoftKeyOrganize")
        Me.SetMenuEditor(Me.MenuItemRight.MenuItems)
      Case Me._settingsIndex
        Me.MenuItemLeft.Enabled = Me._smb.CanGoPreviousMenu
        Me.MenuItemLeft.Text = GetLanguageString("Back")
        Me.SetMenuSettings(Me.MenuItemRight.MenuItems)
      Case Else ' Clear out both soft keys because we are in some unknown state
        Me.MenuItemLeft.Enabled = False
        Me.MenuItemLeft.Text = ""
        Me.MenuItemLeft.MenuItems.Clear()
        Me.MenuItemRight.Enabled = False
        Me.MenuItemRight.Text = ""
        Me.MenuItemRight.MenuItems.Clear()
    End Select
  End Sub

  Private Sub ControlContainerMain_ButtonDown(ByVal func As Integer, ByVal param As Integer) Handles ControlContainerMain.ButtonDown
    Me.ProcessButtonDown(func, param)
  End Sub

  Private Sub ControlContainerMain_ButtonDrag(ByVal func As Integer, ByVal param As Integer) Handles ControlContainerMain.ButtonDrag
    Me.ProcessButtonDrag(func, param)
  End Sub

  Private Sub ControlContainerMain_ButtonUp(ByVal func As Integer, ByVal param As Integer) Handles ControlContainerMain.ButtonUp
    Me.ProcessButtonUp(func, param)
  End Sub

  Private Sub ControlContainerMain_RefreshPlayStates() Handles ControlContainerMain.RefreshPlayStates
    Me.UpdateStatusMessage()
    Me.RefreshPlaybackStates()
  End Sub

  Private Sub ControlContainerMain_DelayedHideFinished() Handles ControlContainerMain.DelayedHideFinished
    If Me._prettyListMiddleShouldBeVisible Then
      Me.PrettyListMiddleShow()
    End If
  End Sub

  Private Sub ProcessButtonPress(ByVal func As ControlFunctions.Functions)
    Me.ProcessButtonPress(func, 0)
  End Sub

  Private Sub ProcessButtonDown(ByVal func As ControlFunctions.Functions)
    Me.ProcessButtonDown(func, 0)
  End Sub

  Private Sub ProcessButtonUp(ByVal func As ControlFunctions.Functions)
    Me.ProcessButtonUp(func, 0)
  End Sub

  Private Sub ProcessButtonPress(ByVal func As ControlFunctions.Functions, ByVal param As Integer)
    Me.ProcessButtonDown(func, param)
    Me.ProcessButtonUp(func, param)
  End Sub

  ''' <summary>
  ''' Returns TRUE if the event is handled, FALSE otherwise
  ''' </summary>
  Private Function ProcessButtonDown(ByVal func As ControlFunctions.Functions, ByVal param As Integer, Optional ByVal skipLongPress As Boolean = False) As Boolean
    Dim handled As Boolean = True

    Select Case func
      Case ControlFunctions.Functions.ListUp
        If Not Me.PrettyListMiddle.Visible Then Me.NowPlayingListShow()
        Me.PrettyListMiddle.NavPreviousPLI()
      Case ControlFunctions.Functions.ListDown
        If Not Me.PrettyListMiddle.Visible Then Me.NowPlayingListShow()
        Me.PrettyListMiddle.NavNextPLI()
      Case ControlFunctions.Functions.ListSelect
        If Me.PrettyListMiddle.Visible Then
          Me.PrettyListMiddle.NavSelectDownPLI()
        End If
      Case ControlFunctions.Functions.ListContext
        If Me.PrettyListMiddle.Visible Then
          Me.PrettyListMiddle.NavSelectDownPLI() ' Context menu starts out like a normal click, so we do a 'selectdown'
        End If



      Case ControlFunctions.Functions.__MULTI_START To ControlFunctions.Functions.__MULTI_END ' Handling all multibuttons
        handled = Me.ProcessButtonDown(ControlFunctions.GetControlFunctionFromMultiFunction(func, Me._lastIsReady, Me._lastIsWait, (Me._lastStatus = MAP_STATUS.PLAY), (Me._lastStatus = MAP_STATUS.PAUSE), Me._AudioEngine.IsStream, Me.PrettyListMiddle.Visible), param, True)



      Case ControlFunctions.Functions.TrackPositionSlider ' Handle the track slider
        If Not Me._AudioEngine.IsStream Then
          Me.TimerSeekSet.Interval = Settings.SeekPreviewWindow
          Me.TimerSeekSet.Enabled = True
          Me.PlaybackLabelTop.OverlayTextHuge = PlaybackLabel.GetFormattedTimerString(ControlButton.TrackSliderPosition, ControlButton.TrackSliderMax)
        End If

      Case Else ' In any case where the down action does NOT have an action (above), return false
        handled = False
    End Select

    If Not skipLongPress AndAlso Me.HasLongPressFunction(func) Then
      ' Store the function and parameter for use in the long press
      Me._lastButtonDownFunc = func
      Me._lastButtonDownParam = param

      ' Start the long press timer
      Me.TimerControlsButtonLongPress.Interval = Settings.LongPressTime
      Me.TimerControlsButtonLongPress.Enabled = True

      If Me.HasLongPressFunctionVisible(func) Then
        ' Set the long press animation settings
        Me._longPressStopWatch.Reset()
        Me._longPressStopWatch.Start()
        Me.TimerLongPressAnimation.Enabled = True
        Me.PlaybackLabelTop.LongPressPercentage = 0.0F
      End If
    End If

    Return handled
  End Function

  Private Sub ProcessButtonDrag(ByVal func As ControlFunctions.Functions, ByVal param As Integer)
    Select Case func
      Case ControlFunctions.Functions.TrackPositionSlider ' Handle the track slider
        Me.PlaybackLabelTop.OverlayTextHuge = PlaybackLabel.GetFormattedTimerString(ControlButton.TrackSliderPosition, ControlButton.TrackSliderMax)
    End Select
  End Sub

  Private Sub ProcessButtonUp(ByVal func As ControlFunctions.Functions, ByVal param As Integer)
    ' Kill the long press timers
    If Me.TimerControlsButtonLongPress.Enabled OrElse Me.TimerLongPressAnimation.Enabled Then
      Me.TimerControlsButtonLongPress.Enabled = False
      Me._longPressStopWatch.Reset()
      Me.TimerLongPressAnimation.Enabled = False
      Me.PlaybackLabelTop.LongPressPercentage = 0.0F
    End If

    ' Note: we only need to look for single function buttons, because multifunction buttons only raise the active single button function they represent
    Select Case func
      Case ControlFunctions.Functions.ListSelect
        If Me.PrettyListMiddle.Visible Then
          Me.PrettyListMiddle.NavSelectUpPLI()
        End If

      Case ControlFunctions.Functions.ListContext
        If Me.PrettyListMiddle.Visible Then
          Me.PrettyListMiddle.NavContextPLI()
        End If

      Case ControlFunctions.Functions.Stop
        ' Stop the track update timer (always make sure that's stopped at this point)
        Me.TimerUpdateTrackPositionInDB.Enabled = False

        ' Store the last position IF we were previously playing
        If Me._AudioEngine.LastButton = MAP_STATUS.PLAY Then
          Me.UpdateDatabaseLastPlayPosition(AVPLPlaylistEntry.CurrentPlayingItem, Me.PlaybackLabelTop.TrackTime)
        End If

        ' Perform stop
        Me._AudioEngine.Stop()
        Me.TimerStreamRetry.Enabled = False
        Me.TimerUpdateTrackPositionInDB.Enabled = False
        Me._streamRetry = Me._streamRetryStart
        Me.PlaybackLabelTop.TrackTime = 0
        ControlButton.TrackSliderPosition = 0
        Me.PlaybackLabelTop.TrackTimeAlternate = -1

        ' Update the stream status message immediately if this is a stream
        If Me._AudioEngine.IsStream Then
          Me.UpdateStatusMessage()
        End If

      Case ControlFunctions.Functions.Close
        ' Ensure we are stopped
        Me.ProcessButtonPress(ControlFunctions.Functions.Stop)

        ' Close the file (if applicable)
        Me._AudioEngine.Close()

        ' Update the current item and status messages
        AVPLPlaylistEntry.CurrentPlayingItem = Nothing
        Me._noItemLoaded = True
        Me._currentAlbumName = String.Empty
        Me._currentTrackNumber = 0
        Me.PlaybackLabelTop.OverlayText = GetLanguageString("NoItemLoaded")
        Me.PlaybackLabelTop.Artist = String.Empty
        Me.PlaybackLabelTop.Title = String.Empty
        Me.PlaybackLabelTop.TrackLength = 0
        Me.PlaybackLabelTop.TrackTime = 0
        Me.PlaybackLabelTop.TrackTimeAlternate = -1
        Me.UpdateStatusMessage()
        Me.RefreshPlaybackStates()

      Case ControlFunctions.Functions.Play
        If Not Me._noItemLoaded Then
          ' Always kill the retry timer, just in case
          Me.TimerStreamRetry.Enabled = False
          Me._streamRetry = Me._streamRetryStart

          If Me._AudioEngine.IsStream Then
            ' We are bootstrapping the laststreamstatus due to the async nature of the audio engine not changing from disconnected to connecting quick enough
            Me._lastStreamStatus = MAP_STREAMING_STATUS.CONNECTING
          End If

          If (Me._playingIndex = Me._musicIndex AndAlso Settings.TrackMusicPosition) _
           OrElse (Me._playingIndex = Me._podcastIndex AndAlso Settings.TrackPodcastPosition) _
          Then
            If Me._lastStatus = MAP_STATUS.STOP Then
              Dim tm As Integer = Me._db.GetLastPlayPosition(AVPLPlaylistEntry.CurrentPlayingItem.FilePath)

              ' Handle the case where the track was previously unloaded
              If Settings.UnloadOnStop Then
                Me._AudioEngine.OpenUnloadedFile()
              End If

              ' Seek to saved position
              Me._AudioEngine.Seek(tm)
              Me.PlaybackLabelTop.TrackTime = tm ' Don't wait for the playback label to be updated, as if we stop too soon, we'll record a track time of zero!
              ControlButton.TrackSliderPosition = tm
              ControlContainerMain.InvalidateSmart()
            End If

            If Settings.FailSafePositionTracking Then
              Me.TimerUpdateTrackPositionInDB.Enabled = True
            End If
          End If

          Me._AudioEngine.Play()
        End If

      Case ControlFunctions.Functions.Pause
        ' Pause the item
        Me._AudioEngine.Pause()

        ' Update the position (make sure it is current!)
        Me._AudioEngine.UpdateTrackPosition()

        ' Store the last position
        If Me._AudioEngine.LastButton = MAP_STATUS.PLAY AndAlso AVPLPlaylistEntry.CurrentPlayingItem IsNot Nothing AndAlso AVPLPlaylistEntry.CurrentPlayingItem.IsFile Then
          Me._db.UpdateLastPlayPosition(AVPLPlaylistEntry.CurrentPlayingItem.FilePath, Me.PlaybackLabelTop.TrackTime)
        End If

      Case ControlFunctions.Functions.PreviousRewind, ControlFunctions.Functions.NextForward
        If TimerFwdRewind.Enabled Then
          Me.TimerFwdRewind.Enabled = False
          Me.PlaybackLabelTop.OverlayText = ""
        Else
          Select Case Me._currentIndex
            Case Me._musicIndex
              Me._currentPlaylist = Me._musicPlaylist
            Case Me._podcastIndex
              Me._currentPlaylist = Me._podcastPlaylist
            Case Me._radioIndex
              Me._currentPlaylist = Me._radioPlaylist
            Case Else
              Select Case Me._playingIndex
                Case Me._musicIndex
                  Me._currentPlaylist = Me._musicPlaylist
                Case Me._podcastIndex
                  Me._currentPlaylist = Me._podcastPlaylist
                Case Me._radioIndex
                  Me._currentPlaylist = Me._radioPlaylist
                Case Else
                  Me._currentPlaylist = Me._musicPlaylist
              End Select
          End Select

          If Me._AudioEngine.IsStream OrElse func = ControlFunctions.Functions.NextForward Then
            Me._streamStopWatch.Reset()
            Me._streamSongStopWatch.Reset()
            Me.LoadPreviousOrNextTrack(func = ControlFunctions.Functions.PreviousRewind, False)
          ElseIf Me.PlaybackLabelTop.TrackTime <= Settings.PreviousButtonResetsTrackTimerAfter Then
            Me.LoadPreviousOrNextTrack(True, False)
          Else ' Previous button in this case resets the track timer to the beginning
            Me._AudioEngine.Seek(0)
          End If
        End If

      Case ControlFunctions.Functions.SkipAhead
        Me._AudioEngine.SkipAhead(param)

      Case ControlFunctions.Functions.SkipBack
        Me._AudioEngine.SkipBack(param)

      Case ControlFunctions.Functions.MemorizeSong
        If Me._AudioEngine.IsStream Then
          If AVPLPlaylistEntry.CurrentPlayingItem IsNot Nothing Then
            Me.SaveToMemorizedSongList(Me._lastArtist, Me._lastTitle, Me._AudioEngine.StationTitle, True)
          End If
        End If

      Case ControlFunctions.Functions.JumpToEntry
        Select Case Me._currentIndex
          Case Me._musicIndex
            If param > 0 AndAlso param <= Me._musicPlaylist.Count Then
              Me.LoadAndPlayPLEFromNowPlayingList(Me._musicPlaylist.Entries(param - 1), Me._musicPlaylist)
            End If
          Case Me._podcastIndex
            If param > 0 AndAlso param <= Me._podcastPlaylist.Count Then
              Me.LoadAndPlayPLEFromNowPlayingList(Me._podcastPlaylist.Entries(param - 1), Me._podcastPlaylist)
            End If
          Case Me._radioIndex
            If param > 0 AndAlso param <= Me._radioPlaylist.Count Then
              Me.LoadAndPlayPLEFromNowPlayingList(Me._radioPlaylist.Entries(param - 1), Me._radioPlaylist)
            End If
        End Select

      Case ControlFunctions.Functions.VolToggle
        If Me.Mute Then
          Me.Mute = False
        Else
          If param <> 0 Then
            Dim stepping As Integer = 100 / param
            Dim cur As Integer = 0

            If stepping > 0 Then ' Volume increases on each press
              If Me.Volume = 100 Then
                Me.Volume = 0
              Else
                While cur < 100
                  If cur > Me.Volume Then
                    Exit While
                  End If
                  cur += stepping
                End While

                If cur > 100 Then cur = 100
                Me.Volume = CByte(cur)
              End If
            ElseIf stepping < 0 Then ' Volume decreases on each press (a stepping value of 0 is an infinite loop, so that's why we don't just do plain "else")
              If Me.Volume = 0 Then
                Me.Volume = 100
              Else
                cur = 100
                While cur > 0
                  If cur < Me.Volume Then
                    Exit While
                  End If
                  cur += stepping ' Stepping is negative here, so we ADD it and don't subtract it
                End While

                If cur < 0 Then cur = 0
                Me.Volume = CByte(cur)
              End If
            End If
          End If
        End If

      Case ControlFunctions.Functions.ScenePrevious
        Me.PreviousScene()

      Case ControlFunctions.Functions.SceneNext
        Me.NextScene()

      Case ControlFunctions.Functions.OpenAudioFile
        Me.OpenFileIntoNowPlaying()

      Case ControlFunctions.Functions.OpenPlaylistFile
        Me.OpenPlaylistIntoNowPlaying()

      Case ControlFunctions.Functions.OpenURL
        Me.OpenURLIntoNowPlaying()

      Case ControlFunctions.Functions.__MULTI_START To ControlFunctions.Functions.__MULTI_END ' Handling all multibuttons
        Me.ProcessButtonUp(ControlFunctions.GetControlFunctionFromMultiFunction(func, Me._lastIsReady, Me._lastIsWait, (Me._lastStatus = MAP_STATUS.PLAY), (Me._lastStatus = MAP_STATUS.PAUSE), Me._AudioEngine.IsStream, Me.PrettyListMiddle.Visible), param)

      Case ControlFunctions.Functions.TrackPositionSlider ' Handle the track slider
        If Not Me._AudioEngine.IsStream AndAlso Me.TimerSeekSet.Enabled Then
          Me.TimerSeekSet.Enabled = False
          Me.TimerSeekSet_Tick(Me, EventArgs.Empty) ' This will do one final seek
          Me.PlaybackLabelTop.OverlayText = ""
        Else
          Me.TimerSeekSet.Enabled = False
          Me.PlaybackLabelTop.OverlayText = ""
        End If
    End Select
  End Sub

  Private Sub ProcessButtonLong(ByVal func As ControlFunctions.Functions)
    Me.ProcessButtonLong(func, 0)
  End Sub

  Private Sub ProcessButtonLong(ByVal func As ControlFunctions.Functions, ByVal param As Integer)
    Dim clearButtons As Boolean = False ' Used when the ButtonUp may not be processed due to another form showing up top or something before that event is fired.

    Select Case func
      Case ControlFunctions.Functions.StopPlayPause ' We don't need to parse this multi into its component parts, because holding it down is always a stop!
        Me.ProcessButtonUp(ControlFunctions.Functions.Stop) ' Send this to the ProcessButtonUp because that's where all the "stop" code lives anyway
        clearButtons = True

      Case ControlFunctions.Functions.PreviousRewind
        If Not Me._AudioEngine.IsStream Then
          Me.TimerFwdRewind.Interval = Settings.RewTimerInterval
          Me._forwardRewindIsRewind = True
          Me.TimerFwdRewind.Enabled = True
        End If

      Case ControlFunctions.Functions.NextForward
        If Not Me._AudioEngine.IsStream Then
          Me.TimerFwdRewind.Interval = Settings.FwdTimerInterval
          Me._forwardRewindIsRewind = False
          Me.TimerFwdRewind.Enabled = True
        End If

      Case ControlFunctions.Functions.VolToggle
        Me.ShowVolumeForm()
        clearButtons = True

      Case ControlFunctions.Functions.__MULTI_START To ControlFunctions.Functions.__MULTI_END ' Handling the rest of the multibuttons
        Me.ProcessButtonLong(ControlFunctions.GetControlFunctionFromMultiFunction(func, Me._lastIsReady, Me._lastIsWait, (Me._lastStatus = MAP_STATUS.PLAY), (Me._lastStatus = MAP_STATUS.PAUSE), Me._AudioEngine.IsStream, Me.PrettyListMiddle.Visible), param)
    End Select

    If clearButtons Then
      Me.ControlContainerMain.ClearButtonStates()
      Me.ControlContainerMain.InvalidateSmart()
    End If
  End Sub

  Public Function HasLongPressFunction(ByVal func As ControlFunctions.Functions) As Boolean
    Select Case func
      Case ControlFunctions.Functions.StopPlayPause, ControlFunctions.Functions.PreviousRewind, ControlFunctions.Functions.NextForward, ControlFunctions.Functions.VolToggle
        Return True
      Case ControlFunctions.Functions.__MULTI_START To ControlFunctions.Functions.__MULTI_END
        Return HasLongPressFunction(ControlFunctions.GetControlFunctionFromMultiFunction(func, Me._lastIsReady, Me._lastIsWait, (Me._lastStatus = MAP_STATUS.PLAY), (Me._lastStatus = MAP_STATUS.PAUSE), Me._AudioEngine.IsStream, Me.PrettyListMiddle.Visible))
    End Select
    Return False
  End Function

  Public Function HasLongPressFunctionVisible(ByVal func As ControlFunctions.Functions) As Boolean
    Select Case func
      Case ControlFunctions.Functions.PreviousRewind, ControlFunctions.Functions.NextForward
        Return False
    End Select
    Return HasLongPressFunction(func)
  End Function

  Private Sub TimerControlsButtonLongPress_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TimerControlsButtonLongPress.Tick
    Me.TimerControlsButtonLongPress.Enabled = False
    Me.ProcessButtonLong(Me._lastButtonDownFunc, Me._lastButtonDownParam)
  End Sub

  Private Function LoadPreviousOrNextTrack(ByVal previous As Boolean, ByVal automatic As Boolean) As Boolean
    Dim lastlaststatus As MAP_STATUS = Me._lastStatus
    Dim lastlastbutton As MAP_STATUS = Me._AudioEngine.LastButton
    Dim doRestoreStatus As Boolean = False
    Dim isLastItemInList As Boolean = False

    Dim playingPlaylist As AVPLPlaylist

    ' Make sure the playing index is valid based on disabled tabs
    Me._playingIndex = Me.GetNowPlayingIndexByIndex(Me._playingIndex)

    Select Case Me._playingIndex
      Case Me._radioIndex
        playingPlaylist = Me._radioPlaylist
      Case Me._podcastIndex
        playingPlaylist = Me._podcastPlaylist
      Case Else
        playingPlaylist = Me._musicPlaylist
    End Select

    If Me._menuItemControlShuffle.Checked Then
      isLastItemInList = playingPlaylist.IsItemLastInShuffleList(AVPLPlaylistEntry.CurrentPlayingItem.GetHashCode)
    Else
      isLastItemInList = playingPlaylist.Entries.Last Is AVPLPlaylistEntry.CurrentPlayingItem
    End If

    If playingPlaylist.Entries.Count <= 0 Then
      Me.ShowOverlayTimedMessage(GetLanguageString("NPEmpty"))
    ElseIf playingPlaylist.Entries.Count = 1 Then
      ' In this case, just reset the seek to 0 (if possible)
      If Not Me._AudioEngine.IsStream Then
        If Not automatic OrElse Me._menuItemControlRepeat.Checked Then
          Me._AudioEngine.Seek(0)
        Else
          Return False ' Cannot repeat!
        End If
      End If
    Else ' More than one entry
      Dim avpleNext As Playlists.AVPLPlaylistEntry = FindPreviousOrNextTrack(playingPlaylist, previous, AVPLPlaylistEntry.CurrentPlayingItem)

      If Not automatic OrElse previous OrElse Not isLastItemInList OrElse Me._menuItemControlRepeat.Checked Then
        Me.LoadPLEIntoAudioEngine(avpleNext)
        doRestoreStatus = True
      Else
        Return False ' Cannot repeat!
      End If
    End If

    If doRestoreStatus AndAlso (lastlaststatus = MAP_STATUS.PLAY OrElse (lastlaststatus = MAP_STATUS.WAIT AndAlso lastlastbutton = MAP_STATUS.PLAY)) Then
      Me.ProcessButtonPress(ControlFunctions.Functions.Play)
    End If

    Return True
  End Function

  Private Function FindPreviousOrNextTrack(ByVal avpl As AVPLPlaylist, ByVal previous As Boolean, ByVal ple As AVPLPlaylistEntry) As AVPLPlaylistEntry
    If avpl IsNot Nothing AndAlso avpl.Entries.Count > 1 Then
      If Me._menuItemControlShuffle.Checked AndAlso avpl IsNot Me._radioPlaylist Then
        ' Find previous/next shuffled item
        If previous Then
          Return avpl.GetPreviousItemShuffled(ple)
        Else
          Return avpl.GetNextItemShuffled(ple)
        End If
      Else
        ' Find sequential previous/next
        For i As Integer = 0 To avpl.Entries.Count - 1
          If avpl.Entries(i) Is ple Then
            If previous Then
              If i > 0 Then
                Return avpl.Entries(i - 1)
              Else
                Return avpl.Entries(avpl.Entries.Count - 1)
              End If
            Else
              If i < avpl.Entries.Count - 1 Then
                Return avpl.Entries(i + 1)
              Else
                Return avpl.Entries(0)
              End If
            End If
          End If
        Next

        ' Oops!  Not found.  Return the first item in the list
        Return avpl.Entries(0)
      End If
    End If
    Return Nothing
  End Function

  Private Sub UpdateDatabaseLastPlayPosition(ByVal avple As Playlists.AVPLPlaylistEntry, ByVal trackTime As Integer)
    If avple IsNot Nothing AndAlso avple.IsFile Then
      Me._db.UpdateLastPlayPosition(avple.FilePath, trackTime)
    End If
  End Sub

  Private Sub RefreshPlaybackStates()
    Static myLastStatus As MAP_STATUS = MAP_STATUS.STOP
    Static myIsStream As Boolean = False
    Static firstPass As Boolean = True
    Static lastCount As Integer = -1

    Dim isReady As Boolean = True
    Dim isPlaying As Boolean = False
    Dim isPaused As Boolean = False
    Dim isWait As Boolean = False

    ' Special override for when we are retrying/connecting
    If Me._AudioEngine.IsStream AndAlso Me._lastStatus = MAP_STATUS.STOP AndAlso Me._AudioEngine.LastButton = MAP_STATUS.PLAY Then
      Me._lastStatus = MAP_STATUS.WAIT
    End If

    If firstPass OrElse myLastStatus <> Me._lastStatus OrElse myIsStream <> Me._AudioEngine.IsStream OrElse Me._noItemLoaded <> Me._lastNoItemLoaded OrElse lastCount <> Me.PlaylistCountForControlFunctions Then
      firstPass = False
      myLastStatus = Me._lastStatus
      myIsStream = Me._AudioEngine.IsStream
      Me._lastNoItemLoaded = Me._noItemLoaded
      lastCount = Me.PlaylistCountForControlFunctions

      Select Case CType(Me._lastStatus, MAP_STATUS)
        Case MAP_STATUS.PLAY
          isPlaying = True
          isPaused = False
          Me.SetStatusMessageAltShow(False)
          Me.TimerTrackCounter.Enabled = True
          If Me._AudioEngine.IsStream Then
            Me._streamStopWatch.Start()
            Me._streamSongStopWatch.Start()
            Me.TimerStatusMessageFlip.Enabled = True
          Else
            Me._streamStopWatch.Stop()
            Me._streamSongStopWatch.Stop()
            If Me._currentAlbumName.Length > 0 Then
              Me.TimerStatusMessageFlip.Enabled = True
            Else
              Me.TimerStatusMessageFlip.Enabled = False
            End If
          End If
        Case MAP_STATUS.PAUSE
          isPlaying = False
          isPaused = True
          Me.SetStatusMessageAltShow(False)
          Me._streamStopWatch.Stop()
          Me._streamSongStopWatch.Stop()
          Me.TimerTrackCounter.Enabled = False
          Me.TimerTrackCounter_Tick(Me, EventArgs.Empty)
        Case MAP_STATUS.STOP
          If AVPLPlaylistEntry.CurrentPlayingItem Is Nothing Then isReady = False
          isPlaying = False
          isPaused = False
          Me._streamStopWatch.Reset()
          Me._streamSongStopWatch.Reset()
          Me.SetStatusMessageAltShow(False)
          Me.TimerTrackCounter.Enabled = False
          Me.TimerTrackCounter_Tick(Me, EventArgs.Empty)
        Case MAP_STATUS.WAIT
          isWait = True
          isPlaying = False
          isPaused = False
          Me.SetStatusMessageAltShow(False)
          Me._streamStopWatch.Stop()
          Me._streamSongStopWatch.Stop()
          Me.TimerTrackCounter.Enabled = False
          Me.TimerTrackCounter_Tick(Me, EventArgs.Empty)
        Case Else
          isReady = True
          isPlaying = False
          isPaused = False
          isWait = False
      End Select

      ' Update states of all buttons
      For Each c As ControlSurface In Me.ControlContainerMain.Surfaces
        For Each s As ControlScene In c.Scenes
          For Each r As ControlRow In s.Rows
            For Each b As ControlButton In r.Buttons
              b.PlayStateCallback(isReady, Not Me._noItemLoaded, isWait, isPlaying, isPaused, Me._AudioEngine.IsStream, lastCount)
            Next
          Next
        Next
      Next

      ' Update states of all menu items
      Dim cnt As Integer = 0
      If Me._currentPlaylist IsNot Nothing Then cnt = Me._currentPlaylist.Count
      Me._menuItemControlPlay.Enabled = ControlFunctions.GetControlFunctionIsActive(ControlFunctions.Functions.Play, isReady, Not Me._noItemLoaded, isWait, isPlaying, isPaused, Me._AudioEngine.IsStream, cnt)
      Me._menuItemControlPause.Enabled = ControlFunctions.GetControlFunctionIsActive(ControlFunctions.Functions.Pause, isReady, Not Me._noItemLoaded, isWait, isPlaying, isPaused, Me._AudioEngine.IsStream, cnt)
      Me._menuItemControlStop.Enabled = ControlFunctions.GetControlFunctionIsActive(ControlFunctions.Functions.Stop, isReady, Not Me._noItemLoaded, isWait, isPlaying, isPaused, Me._AudioEngine.IsStream, cnt)
      Me._menuItemControlClose.Enabled = ControlFunctions.GetControlFunctionIsActive(ControlFunctions.Functions.Close, isReady, Not Me._noItemLoaded, isWait, isPlaying, isPaused, Me._AudioEngine.IsStream, cnt)
      Me._menuItemControlPrevious.Enabled = ControlFunctions.GetControlFunctionIsActive(ControlFunctions.Functions.PreviousRewind, isReady, Not Me._noItemLoaded, isWait, isPlaying, isPaused, Me._AudioEngine.IsStream, cnt)
      Me._menuItemControlNext.Enabled = ControlFunctions.GetControlFunctionIsActive(ControlFunctions.Functions.NextForward, isReady, Not Me._noItemLoaded, isWait, isPlaying, isPaused, Me._AudioEngine.IsStream, cnt)

      ' Update last states
      Me._lastIsReady = isReady
      Me._lastIsWait = isWait

      ' Invalidate the controls
      Me.ControlContainerMain.InvalidateSmart()
    End If
  End Sub

  Private Sub AudioEngine_RefreshStreamStatusTitle() Handles _AudioEngine.RefreshStreamStatusTitle
    Me.UpdateStatusMessage()
  End Sub

  Private Sub UpdateStatusMessage()
    If Me._AudioEngine.IsStream Then
      Select Case Me._lastStreamStatus
        Case MAP_STREAMING_STATUS.BUFFERING
          Me._dsm.PausePreventDeviceSleep = False ' Unpause sleep manager in any "positive" state for the streams
          Me.PlaybackLabelTop.StatusMessage = GetLanguageString("BufferingStream")
        Case MAP_STREAMING_STATUS.CONNECTED
          Me._dsm.PausePreventDeviceSleep = False ' Unpause sleep manager in any "positive" state for the streams
          Me._streamRetry = Me._streamRetryStart
          Me._streamRetryFlushed = False
          If Me._showStatusMessageAlt Then
            Me.PlaybackLabelTop.StatusMessage = Me._AudioEngine.StationTitle
          Else
            Me.PlaybackLabelTop.StatusMessage = GetLanguageString("Streaming") & GetBitrateString()
          End If
        Case MAP_STREAMING_STATUS.CONNECTING
          Me._dsm.PausePreventDeviceSleep = False ' Unpause sleep manager in any "positive" state for the streams
          If Me._AudioEngine.LastButton = MAP_STATUS.STOP Then
            Me.PlaybackLabelTop.StatusMessage = GetLanguageString("CancelConnection")
          Else
            Me.PlaybackLabelTop.StatusMessage = GetLanguageString("ConnectingToStream")
          End If
        Case Else ' DISCONNECTED/DISBALED
          If Me._AudioEngine.LastButton <> MAP_STATUS.STOP Then
            If Me._streamRetry <= 0 Then
              ' Retry using the same URL
              Me._AudioEngine.Stop(True)
              Me._lastStreamStatus = MAP_STREAMING_STATUS.CONNECTING
              Me._AudioEngine.Play(False)

              Me._streamRetry += 1
            ElseIf Not Me.TimerStreamRetry.Enabled Then
              Me.TimerStreamRetry.Enabled = False ' Stop the counter for now

              ' Multiple failures: load the next URL and wait the number of seconds for whatever retry we are on (limit is max)
              If Me._streamRetry > Me._streamRetryMaxTicks Then
                Me._streamRetry = Me._streamRetryMaxTicks
                If Not Me._streamRetryFlushed Then
                  Try
                    ' Reset the audio engine and flush DNS, just in case...
                    Me._AudioEngine.EndAudioEngine()
                    ExtendedNetworkMethods.FlushDNSResolverCache()
                    Me._AudioEngine.StartAudioEngine()
                    Me._AudioEngine.ReOpen()
                  Catch ex As Exception
                    WriteToErrorLog("AvianPlayMain.UpdateStatusMessage :: Unable to flush DNS resolver cache :: " & ex.ToString)
                  End Try
                  Me._streamRetryFlushed = True
                End If
              End If

              Me._streamRetryTicks = Me._streamRetry
              Me.PlaybackLabelTop.StatusMessage = GetLanguageString("Retrying").Replace("%", Me._streamRetryTicks.ToString)

              Me.TimerStreamRetry.Interval = 1000
              Me.TimerStreamRetry.Enabled = True
              Me._streamRetry += 1
            End If
          Else ' We are stopped
            Me.TimerStreamRetry.Enabled = False

            If Me._showStatusMessageAlt Then
              Me.PlaybackLabelTop.StatusMessage = Me._AudioEngine.StationTitle
            Else
              Me.PlaybackLabelTop.StatusMessage = GetLanguageString("Stopped")
            End If

            ' This double checks (and corrects) an inconsistent state that can pop up due to stream connection errors and timing with pressing the stop button/retrying.  It's a kludge, but it works!
            If Me._lastStatus <> MAP_STATUS.STOP Then
              Me._lastStatus = MAP_STATUS.STOP
              Me.RefreshPlaybackStates()
            End If

            ' Pause the device sleep manager because we are stopped at this point
            Me._dsm.PausePreventDeviceSleep = True
          End If
      End Select
    Else
      Select Case CType(Me._lastStatus, MAP_STATUS)
        Case MAP_STATUS.PLAY
          Me._dsm.PausePreventDeviceSleep = False ' Unpause the sleep manager when playback starts
          If Me._showStatusMessageAlt AndAlso Me._currentAlbumName.Length > 0 Then
            Me.PlaybackLabelTop.StatusMessage = Me._currentAlbumName
          ElseIf Me._currentTrackNumber > 0 Then
            Me.PlaybackLabelTop.StatusMessage = GetLanguageString("PlayingTrackNum").Replace("%", Me._currentTrackNumber.ToString) & Me.GetBitrateString
          Else
            Me.PlaybackLabelTop.StatusMessage = GetLanguageString("Playing") & Me.GetBitrateString
          End If
        Case MAP_STATUS.PAUSE
          Me._dsm.PausePreventDeviceSleep = True ' Pause the sleep manager when playback is paused
          If Me._showStatusMessageAlt AndAlso Me._currentAlbumName.Length > 0 Then
            Me.PlaybackLabelTop.StatusMessage = Me._currentAlbumName
          ElseIf Me._currentTrackNumber > 0 Then
            Me.PlaybackLabelTop.StatusMessage = GetLanguageString("PausedTrackNum").Replace("%", Me._currentTrackNumber.ToString) & Me.GetBitrateString
          Else
            Me.PlaybackLabelTop.StatusMessage = GetLanguageString("Paused") & Me.GetBitrateString
          End If
        Case MAP_STATUS.STOP
          ' We reached the end of the file or just a regular stop?
          If Me._AudioEngine.LastButton = MAP_STATUS.PLAY AndAlso Me._AudioEngine.NextTrackReady Then
            Dim doPlay As Boolean = True

            ' Set the current file position to 0 in the DB because we have reached EOF
            If AVPLPlaylistEntry.CurrentPlayingItem IsNot Nothing AndAlso AVPLPlaylistEntry.CurrentPlayingItem.IsFile Then
              Me.TimerUpdateTrackPositionInDB.Enabled = False
              Me._db.UpdateLastPlayPosition(AVPLPlaylistEntry.CurrentPlayingItem.FilePath, 0)
            End If

            ' Open and play the next item, IF the seek timer is not activated
            If Not Me.TimerSeekSet.Enabled Then
              Dim lastTrack As Playlists.AVPLPlaylistEntry = AVPLPlaylistEntry.CurrentPlayingItem ' store the last playing item

              If Not Me.LoadPreviousOrNextTrack(False, True) Then         ' Load up and play the next item
                ' We reached the end of the playlist and no repeat, so we need to officially stop
                Me.PlaybackLabelTop.StatusMessage = GetLanguageString("Stopped")
                Me.ProcessButtonPress(ControlFunctions.Functions.Stop) ' We do this to set all the button states to stop
                doPlay = False
              End If

              Me.UpdateDatabaseLastPlayPosition(lastTrack, 0) ' Set the last item to seek of '0' in the database (if applicable)
            End If

            If doPlay AndAlso AVPLPlaylistEntry.CurrentPlayingItem IsNot Nothing Then
              Me.ProcessButtonPress(ControlFunctions.Functions.Play)
            Else
              Me._dsm.PausePreventDeviceSleep = True ' Playback has ended because "doplay" is negative or the currentplayingitem is null
            End If
          Else
            If Me._showStatusMessageAlt AndAlso Me._currentAlbumName.Length > 0 Then
              Me.PlaybackLabelTop.StatusMessage = Me._currentAlbumName
            ElseIf Me._currentTrackNumber > 0 Then
              Me.PlaybackLabelTop.StatusMessage = GetLanguageString("StoppedTrackNum").Replace("%", Me._currentTrackNumber.ToString)
            Else
              Me.PlaybackLabelTop.StatusMessage = GetLanguageString("Stopped")
            End If
          End If
        Case MAP_STATUS.WAIT
          Me.PlaybackLabelTop.StatusMessage = GetLanguageString("PleaseWait")
      End Select
    End If
  End Sub

  Private Sub TimerStreamRetry_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TimerStreamRetry.Tick
    Static playOnNext As Boolean = False

    Me.TimerStreamRetry.Interval = 1000

    If Me._lastStreamStatus <> MAP_STREAMING_STATUS.DISCONNECTED Then
      Me.TimerStreamRetry.Enabled = False
      playOnNext = False
    ElseIf Me._streamRetryTicks > 1 Then
      Me._streamRetryTicks -= 1
      Me.PlaybackLabelTop.StatusMessage = GetLanguageString("Retrying").Replace("%", Me._streamRetryTicks.ToString)
      playOnNext = False
    ElseIf playOnNext Then
      Me.TimerStreamRetry.Enabled = False
      Me.LoadNextCurrentPlayingItemStreamURL()
      Me._AudioEngine.Stop(True)
      Me._lastStreamStatus = MAP_STREAMING_STATUS.CONNECTING
      Me._AudioEngine.Play(False)
      playOnNext = False
    Else
      Me.PlaybackLabelTop.StatusMessage = GetLanguageString("ConnectingToStream")
      Me.TimerStreamRetry.Interval = 100
      playOnNext = True ' We do this for 1/10 a second to give the UI a chance to catch up to the fact that we are reconnecting to the stream
    End If
  End Sub



  Private Function GetBitrateString()
    If Me._statusBitrate > 0 Then
      Return " " & Me._statusBitrate.ToString & "k"
    Else
      Return ""
    End If
  End Function



  Private Sub PrettyListMiddle_NewItemsAssignedBegin(ByVal sender As Object) Handles PrettyListMiddle.NewItemsAssignedBegin
    Try
      If Me._nowPlayingUpdateThread IsNot Nothing Then
        Me._nowPlayingUpdateThread.Abort()
        Me._nowPlayingUpdateThread = Nothing
      End If
    Catch ex As Exception
      ErrorLog.WriteToErrorLog("PrettyListMiddle_NewItemsAssignedBegin :: " & ex.ToString)
    End Try
  End Sub

  Private Sub PrettyListMiddle_NewItemsAssignedEnd(ByVal sender As Object) Handles PrettyListMiddle.NewItemsAssignedEnd
    Dim isInLibraryMenu As Boolean = False

    Me._nowPlayingEntries.Clear()
    Me._thumbnailEntries.Clear()
    Me.PrettyListMiddle.Filter = ""

    If Playlists.AVPLPlaylistEntry.AlbumArtListDisplayMode <> AVPLPlaylistEntry.AlbumArtListDisplayModeEnum.NoAlbumArt Then
      If Me.PrettyListMiddle.Items IsNot Nothing Then
        If Me.PrettyListMiddle.Items Is Me._lmb.Items Then
          isInLibraryMenu = True
          For Each le As Library.LibraryEntry In Me._lmb.Items
            If le.AVPLRef IsNot Nothing AndAlso le.AVPLRef.IsFile AndAlso le.AVPLRef.HasDBThumbnail Then
              Dim td As New ThumbnailData
              td.filepath = le.AVPLRef.FilePath
              td.bmp = Nothing
              Me._thumbnailEntries.Add(td)
            End If
          Next
        ElseIf Me.PrettyListMiddle.Items Is Me._musicPlaylist Then
          For Each ae As AVPLPlaylistEntry In Me._musicPlaylist
            Me._nowPlayingEntries.Add(ae.FilePath)
            If ae.HasDBThumbnail Then
              Dim td As New ThumbnailData
              td.filepath = ae.FilePath
              td.bmp = Nothing
              Me._thumbnailEntries.Add(td)
            End If
          Next
        ElseIf Me.PrettyListMiddle.Items Is Me._podcastPlaylist Then
          For Each ae As AVPLPlaylistEntry In Me._podcastPlaylist
            Me._nowPlayingEntries.Add(ae.FilePath)
            If ae.HasDBThumbnail Then
              Dim td As New ThumbnailData
              td.filepath = ae.FilePath
              td.bmp = Nothing
              Me._thumbnailEntries.Add(td)
            End If
          Next
        ElseIf Me.PrettyListMiddle.Items Is Me._radioPlaylist Then
          For Each ae As AVPLPlaylistEntry In Me._radioPlaylist
            Me._nowPlayingEntries.Add(ae.FilePath)
            If ae.HasDBThumbnail Then
              Dim td As New ThumbnailData
              td.filepath = ae.FilePath
              td.bmp = Nothing
              Me._thumbnailEntries.Add(td)
            End If
          Next
        ElseIf Me.PrettyListMiddle.Items Is Me._editorPlaylist Then
          For Each ae As AVPLPlaylistEntry In Me._editorPlaylist
            Me._nowPlayingEntries.Add(ae.FilePath)
            If ae.HasDBThumbnail Then
              Dim td As New ThumbnailData
              td.filepath = ae.FilePath
              td.bmp = Nothing
              Me._thumbnailEntries.Add(td)
            End If
          Next
        End If

        If Me._nowPlayingEntries.Count > 0 OrElse Me._thumbnailEntries.Count > 0 OrElse isInLibraryMenu Then
          Me._nowPlayingUpdateThread = New Threading.Thread(AddressOf RefreshNowPlayingDataThread)
          Me._nowPlayingUpdateThread.Name = "NowPlayingUpdateThread"
          Me._nowPlayingUpdateThread.Priority = Threading.ThreadPriority.BelowNormal

          SyncLock MainThreadPool
            MainThreadPool.Add(_nowPlayingUpdateThread)
          End SyncLock

          Me._nowPlayingUpdateThread.Start()
        End If
      End If
    End If
  End Sub

  Private Sub RefreshNowPlayingDataThread()
    Try
      Dim cacheSize As Integer = Settings.AlbumArtListCache

      ' Initalize the database connection
      Using db As New AvianPlayDB
        ' Update all thumbnails (most important)
        For Each td As ThumbnailData In Me._thumbnailEntries
          ' Try to set image from thumbnail cache
          FindInThumbnailCache(td)

          ' If no success, find thumbnail in DB
          If td.bmp Is Nothing Then
            FindThumbnailInDB(db, td, cacheSize)
          End If

          ' If we found it, set it!
          If td.bmp IsNot Nothing Then
            Me.UpdateThumbnailItem(td)
          End If
        Next

        ' Check to see if the display mode has changed to "no album art"
        If Playlists.AVPLPlaylistEntry.AlbumArtListDisplayMode <> AVPLPlaylistEntry.AlbumArtListDisplayModeEnum.NoAlbumArt Then
          ' Now look for thumbnail queries (like album art for albums)
          If Me.PrettyListMiddle.Items Is Me._lmb.Items Then
            Dim cmd As SqlCeCommand = db.GetInternalCommandForAdHocCommand
            For Each le As Library.LibraryEntry In Me._lmb.Items
              If le.ThumbnailQuery IsNot Nothing Then
                cmd.Parameters.Clear()
                cmd.CommandText = le.ThumbnailQuery
                For Each p As SqlCeParameter In le.ThumbnailParameters
                  cmd.Parameters.Add(p)
                Next
                Using result = cmd.ExecuteReader
                  If result.Read() AndAlso result.FieldCount = 1 Then
                    ' Fetch the filepath from the query
                    Dim td As ThumbnailData
                    td.filepath = result.GetString(0)
                    td.bmp = Nothing

                    ' Try to set image from thumbnail cache
                    FindInThumbnailCache(td)

                    ' If no success, find thumbnail in DB
                    If td.bmp Is Nothing Then
                      FindThumbnailInDB(db, td, cacheSize)
                    End If

                    ' If we found it, set it!
                    If td.bmp IsNot Nothing Then
                      Me.UpdateThumbnailOnLibraryEntry(le, td.bmp)
                    End If
                  End If
                End Using
              End If
            Next
          End If
        End If

        ' Now update other information, like album, albumartist, etc.
        If Me.PrettyListMiddle.Items IsNot Me._lmb.Items Then
          For Each filepath As String In Me._nowPlayingEntries
            Using aft As AvianPlayDB.AudioFileTable = db.GetAudioFileDBData(filepath)
              If aft IsNot Nothing Then
                Me.UpdateMiscDataItem(filepath, aft.Album, aft.AlbumArtist, aft.Genre)
              End If
            End Using
          Next
        End If
      End Using
    Catch ex As InvalidOperationException
      ' This is thrown when the collection is modified outside this thread - this can happen when the library menu builder modifies it's collection out of sync of this thread.  No need to log this unless a user is experiencing an issue here.
    Catch ex As Threading.ThreadAbortException
      ' Always safe to ignore this -- the thread is being terminated
    Catch ex As Exception
      ' Eat all exceptions from the UI, but report to log as long as they aren't sql CE exceptions
      If ex.GetType IsNot GetType(System.Data.SqlServerCe.SqlCeException) Then
        ErrorLog.WriteToErrorLog("RefreshThumbnailThread :: " & ex.ToString)
      End If
    Finally
      SyncLock MainThreadPool
        MainThreadPool.Remove(Me._nowPlayingUpdateThread)
      End SyncLock

      ' Set the thead reference to null to notify that we are done processing
      Me._nowPlayingUpdateThread = Nothing
    End Try
  End Sub

  Private Sub FindInThumbnailCache(ByRef td As ThumbnailData)
    ' Walk through all cached thumbnails
    Dim tc As ThumbnailData
    For i As Integer = 0 To Me._thumbnailCache.Count - 1
      tc = Me._thumbnailCache(i)
      If String.Compare(td.filepath, tc.filepath, True) = 0 Then
        ' Found the thumbnail, move it to top
        Me._thumbnailCache.RemoveAt(i)
        Me._thumbnailCache.Add(tc)
        td.bmp = tc.bmp
        Exit For
      End If
    Next
  End Sub

  Private Sub FindThumbnailInDB(ByVal db As AvianPlayDB, ByRef td As ThumbnailData, ByVal cacheSize As Integer)
    td.bmp = db.GetAlbumArtThumbnailByFilePath(td.filepath)
    If td.bmp IsNot Nothing AndAlso cacheSize > 0 Then
      While Me._thumbnailCache.Count >= cacheSize
        Me._thumbnailCache(0).bmp.Dispose()
        Me._thumbnailCache.RemoveAt(0)
      End While
      Me._thumbnailCache.Add(td)
    End If
  End Sub


  Private Delegate Sub UpdateThumbnailOnLibraryEntryDelegate(ByVal le As Library.LibraryEntry, ByVal bmp As Bitmap)
  Private Sub UpdateThumbnailOnLibraryEntry(ByVal le As Library.LibraryEntry, ByVal bmp As Bitmap)
    If Me.InvokeRequired Then
      Dim del As New UpdateThumbnailOnLibraryEntryDelegate(AddressOf UpdateThumbnailOnLibraryEntry)
      Me.Invoke(del, New Object() {le, bmp})
    Else
      If le IsNot Nothing Then
        le.Thumbnail = bmp.Clone
        Me.PrettyListMiddle.InvalidateSmart()
      End If
    End If
  End Sub

  Private Delegate Sub UpdateThumbnailItemDelegate(ByVal td As ThumbnailData)
  Private Sub UpdateThumbnailItem(ByVal td As ThumbnailData)
    If Me.InvokeRequired Then
      Dim del As New UpdateThumbnailItemDelegate(AddressOf UpdateThumbnailItem)
      Me.Invoke(del, New Object() {td})
    Else
      If Me.PrettyListMiddle.Items IsNot Nothing Then
        If Me.PrettyListMiddle.Items Is Me._lmb.Items Then
          For Each le As Library.LibraryEntry In Me._lmb.Items
            If le.AVPLRef IsNot Nothing AndAlso le.AVPLRef.FilePath = td.filepath Then
              le.AVPLRef.Thumbnail = td.bmp
              Me.PrettyListMiddle.InvalidateSmart()
              Exit For
            End If
          Next
        ElseIf Me.PrettyListMiddle.Items Is Me._musicPlaylist Then
          For Each ae As AVPLPlaylistEntry In Me._musicPlaylist
            If ae.FilePath = td.filepath Then
              ae.Thumbnail = td.bmp.Clone
              Me.PrettyListMiddle.InvalidateSmart()
              Exit For
            End If
          Next
        ElseIf Me.PrettyListMiddle.Items Is Me._podcastPlaylist Then
          For Each ae As AVPLPlaylistEntry In Me._podcastPlaylist
            If ae.FilePath = td.filepath Then
              ae.Thumbnail = td.bmp
              Me.PrettyListMiddle.InvalidateSmart()
              Exit For
            End If
          Next
        ElseIf Me.PrettyListMiddle.Items Is Me._radioPlaylist Then
          For Each ae As AVPLPlaylistEntry In Me._radioPlaylist
            If ae.FilePath = td.filepath Then
              ae.Thumbnail = td.bmp
              Me.PrettyListMiddle.InvalidateSmart()
              Exit For
            End If
          Next
        ElseIf Me.PrettyListMiddle.Items Is Me._editorPlaylist Then
          For Each ae As AVPLPlaylistEntry In Me._editorPlaylist
            If ae.FilePath = td.filepath Then
              ae.Thumbnail = td.bmp
              Me.PrettyListMiddle.InvalidateSmart()
              Exit For
            End If
          Next
        End If
      End If
    End If
  End Sub

  Private Delegate Sub UpdateMiscDataItemDelegate(ByVal filepath As String, ByVal album As String, ByVal albumArtist As String, ByVal genre As String)
  Private Sub UpdateMiscDataItem(ByVal filepath As String, ByVal album As String, ByVal albumArtist As String, ByVal genre As String)
    If Me.InvokeRequired Then
      Dim del As New UpdateMiscDataItemDelegate(AddressOf UpdateMiscDataItem)
      Me.Invoke(del, New Object() {filepath, album, albumArtist, genre})
    Else
      If Me.PrettyListMiddle.Items IsNot Nothing Then
        If Me.PrettyListMiddle.Items Is Me._musicPlaylist Then
          For Each ae As AVPLPlaylistEntry In Me._musicPlaylist
            If ae.FilePath = filepath Then
              ae.Album = album
              ae.AlbumArtist = albumArtist
              ae.Genre = genre
              Me.PrettyListMiddle.InvalidateSmart()
              Exit For
            End If
          Next
        ElseIf Me.PrettyListMiddle.Items Is Me._podcastPlaylist Then
          For Each ae As AVPLPlaylistEntry In Me._podcastPlaylist
            If ae.FilePath = filepath Then
              ae.Album = album
              ae.AlbumArtist = albumArtist
              ae.Genre = genre
              Me.PrettyListMiddle.InvalidateSmart()
              Exit For
            End If
          Next
        ElseIf Me.PrettyListMiddle.Items Is Me._radioPlaylist Then
          For Each ae As AVPLPlaylistEntry In Me._radioPlaylist
            If ae.FilePath = filepath Then
              ae.Album = album
              ae.AlbumArtist = albumArtist
              ae.Genre = genre
              Me.PrettyListMiddle.InvalidateSmart()
              Exit For
            End If
          Next
        ElseIf Me.PrettyListMiddle.Items Is Me._editorPlaylist Then
          For Each ae As AVPLPlaylistEntry In Me._editorPlaylist
            If ae.FilePath = filepath Then
              ae.Album = album
              ae.AlbumArtist = albumArtist
              ae.Genre = genre
              Me.PrettyListMiddle.InvalidateSmart()
              Exit For
            End If
          Next
        End If
      End If
    End If
  End Sub

  Private Sub PrettyListMiddle_ItemMouseDown(ByVal sender As Object, ByVal clickedItem As MobilePrettyGui.iPrettyListItem, ByRef allowedToLongClick As Boolean) Handles PrettyListMiddle.ItemMouseDown
    Me.PrettyListMiddle.LongclickInterval = Settings.LongPressTime
    allowedToLongClick = False

    If clickedItem.GetType Is GetType(Playlists.AVPLPlaylistEntry) Then
      allowedToLongClick = True
    ElseIf clickedItem.GetType Is GetType(Library.LibraryEntry) Then
      Dim idx = Me._lmb.Items.IndexOf(clickedItem) ' Find the index of the item raising the long click
      If idx >= 0 Then
        If Me._lmb.Items(idx).AVPLRef IsNot Nothing OrElse Me._lmb.Items(idx).IsPlaylist Then
          allowedToLongClick = True
        End If
      End If
    End If

    If allowedToLongClick Then
      ' Set the long press animation settings
      Me._longPressStopWatch.Reset()
      Me._longPressStopWatch.Start()
      Me.TimerLongPressAnimation.Enabled = True
      Me.PlaybackLabelTop.LongPressPercentage = 0.0F
    End If
  End Sub

  Private Sub PrettyListMiddle_ItemMouseUp(ByVal sender As Object, ByVal clickedItem As MobilePrettyGui.iPrettyListItem) Handles PrettyListMiddle.ItemMouseUp
    ' Kill the long press timers
    Me.TimerControlsButtonLongPress.Enabled = False
    Me._longPressStopWatch.Reset()
    Me.TimerLongPressAnimation.Enabled = False
    Me.PlaybackLabelTop.LongPressPercentage = 0.0F
  End Sub

  Private Sub PrettyListMiddle_ItemMouseScroll(ByVal sender As Object, ByVal clickedItem As MobilePrettyGui.iPrettyListItem) Handles PrettyListMiddle.ItemMouseScroll
    ' Kill the long press timers
    Me.TimerControlsButtonLongPress.Enabled = False
    Me._longPressStopWatch.Reset()
    Me.TimerLongPressAnimation.Enabled = False
    Me.PlaybackLabelTop.LongPressPercentage = 0.0F
  End Sub

  Private Sub PrettyListMiddle_ItemClicked(ByVal sender As Object, ByVal pli As MobilePrettyGui.iPrettyListItem, ByVal isLongClick As Boolean) Handles PrettyListMiddle.ItemClicked
    If isLongClick Then
      If Me.PrettyListMiddle.Visible Then
        ' Now Playing...
        If Me._currentIndex = Me._musicIndex OrElse Me._currentIndex = Me._podcastIndex OrElse Me._currentIndex = Me._radioIndex Then
          Dim npList = Me.ActiveNowPlayingList ' Get the currently active list (the one visible to the user)
          If npList IsNot Nothing Then
            Dim idx = npList.Entries.IndexOf(pli) ' Find the index of the item raising the long click
            If idx >= 0 Then
              ' Find the entry based on the index found above
              Me._contextMenuAVPLE = npList.Entries(idx)

              ' Show the context menu
              Me.ContextMenuNowPlayingItem.Show(Me.PrettyListMiddle, Me.PrettyListMiddle.PointToClient(Windows.Forms.Form.MousePosition))

              ' Refresh the list since the item should be unselected now
              Me.PrettyListMiddle.InvalidateSmart()
            End If
          End If
        ElseIf Me._currentIndex = Me._editorIndex Then
          If Me._editorPlaylist IsNot Nothing Then
            Dim idx = Me._editorPlaylist.Entries.IndexOf(pli) ' Find the index of the item raising the long click
            If idx >= 0 Then
              ' Find the entry based on the index found above
              Me._contextMenuAVPLE = Me._editorPlaylist.Entries(idx)

              ' Show the context menu
              Me.ContextMenuEditorItem.Show(Me.PrettyListMiddle, Me.PrettyListMiddle.PointToClient(Windows.Forms.Form.MousePosition))

              ' Refresh the list since the item should be unselected now
              Me.PrettyListMiddle.InvalidateSmart()
            End If
          End If
        ElseIf Me._currentIndex = Me._libraryIndex Then
          Dim idx = Me._lmb.Items.IndexOf(pli) ' Find the index of the item raising the long click
          If idx >= 0 Then
            If Me._lmb.Items(idx).AVPLRef IsNot Nothing Then
              ' Find the entry based on the index found above
              Me._contextMenuAVPLE = Me._lmb.Items(idx).AVPLRef

              ' Show the context menu
              Me.ContextMenuLibraryAVPLItem.Show(Me.PrettyListMiddle, Me.PrettyListMiddle.PointToClient(Windows.Forms.Form.MousePosition))

              ' Refresh the list since the item should be unselected now
              Me.PrettyListMiddle.InvalidateSmart()
            ElseIf Me._lmb.Items(idx).IsPlaylist Then
              ' Fetch the playlist based on the index found above
              Me._contextMenuPlaylist = Me._lmb.Items(idx).PlaylistPath

              ' Show the context menu
              Me.ContextMenuLibraryPlaylistItem.Show(Me.PrettyListMiddle, Me.PrettyListMiddle.PointToClient(Windows.Forms.Form.MousePosition))

              ' Refresh the list since the item should be unselected now
              Me.PrettyListMiddle.InvalidateSmart()
            End If
          End If
        End If
      End If
    Else
      Select Case Me._currentIndex
        Case Me._libraryIndex
          Dim idx = Me._lmb.Items.IndexOf(pli)
          If idx >= 0 Then
            Dim smk As String = Me._lmb.Items(idx).SubmenuKey.Trim
            Dim plp As String = Me._lmb.Items(idx).PlaylistPath.Trim
            Me._currentPlaylist = Me.GetNowPlayingPlaylistByIndex(Me._musicIndex)
            Me._playingIndex = Me.GetNowPlayingIndexByIndex(Me._musicIndex)

            If Me._lmb.Items(idx).AVPLRef IsNot Nothing Then
              ' Which playlist do we use?  (depends on if it's music, podcast, or radio)
              Select Case Me._lmb.Items(idx).AVPLRef.Type
                Case AVPLPlaylistEntry.EntryType.File
                  If Me._db.IsFilePodcast(Me._lmb.Items(idx).AVPLRef.FilePath) Then
                    Me._currentPlaylist = Me.GetNowPlayingPlaylistByIndex(Me._podcastIndex)
                    Me._playingIndex = Me.GetNowPlayingIndexByIndex(Me._podcastIndex)
                  Else
                    Me._currentPlaylist = Me.GetNowPlayingPlaylistByIndex(Me._musicIndex)
                    Me._playingIndex = Me.GetNowPlayingIndexByIndex(Me._musicIndex)
                  End If
                Case AVPLPlaylistEntry.EntryType.Stream
                  Me._currentPlaylist = Me.GetNowPlayingPlaylistByIndex(Me._radioIndex)
                  Me._playingIndex = Me.GetNowPlayingIndexByIndex(Me._radioIndex)
              End Select

              ' Abort a stream refresh (if one is going)
              Me._currentPlaylist.AbortRefreshStreamURLs()

              ' Clear the current playlist
              Me._currentPlaylist.Entries.Clear()

              ' Set the refresh paramters for the future refresh of this particular set of items
              If Me._lmb.LastIsQuery Then
                Me._currentPlaylist.LastFileIsQuery = True
                Me._currentPlaylist.LastFileQueryWhereString = Me._lmb.LastQueryWhere.SQLCEWhereClause
                Me._currentPlaylist.LastFileQueryWhereParameters = Me._lmb.LastQueryWhere.SQLCEParameter
                Me._currentPlaylist.LastFileQueryOrder = Me._lmb.LastQueryOrder
                Me._currentPlaylist.LastFilePath = ""
              Else
                Me._currentPlaylist.LastFileIsQuery = False
                Me._currentPlaylist.LastFileQueryWhereString = ""
                Me._currentPlaylist.LastFileQueryWhereParameters = Nothing
                Me._currentPlaylist.LastFileQueryOrder = ""
                Me._currentPlaylist.LastFilePath = Me._lmb.LastFilePath
              End If

              ' Stores the item that should be loaded and played
              Dim pple As AVPLPlaylistEntry = Nothing

              ' Store whether the list is filtered
              Dim listIsNotFiltered As Boolean = Me.PrettyListMiddle.Filter.Count <= 0

              ' Set the current library list entries as the current playlist
              For i = 0 To Me._lmb.Items.Count - 1
                If Me._lmb.Items(i).AVPLRef IsNot Nothing AndAlso (listIsNotFiltered OrElse Me.PrettyListMiddle.IsItemInFilteredList(Me._lmb.Items(i))) Then
                  Dim newEntry As New AVPLPlaylistEntry(Me._lmb.Items(i).AVPLRef)
                  If i = idx Then
                    pple = newEntry
                  End If
                  Me._currentPlaylist.Entries.Add(newEntry)
                End If
              Next

              ' Save the Now Playing Cache
              Me.SaveNPCacheFiles(Me._currentPlaylist)

              ' Load the selected song into the audio engine
              If pple IsNot Nothing Then
                Me.LoadPLEIntoAudioEngine(pple)
                Me.ReshuffleCurrentPlaylist() ' When we are directly selecting and loading a new item, reshuffle the list so the current item becomes the 'start' item
              End If

              ' Jump to the appropriate tab
              Me.SetSlidingButtonStripIndex(Me._playingIndex, False)

              ' Press Play to start playing the selected song
              Me.ProcessButtonPress(ControlFunctions.Functions.Play)

              ' Start stream refresh thread (if applicable)
              Me._currentPlaylist.RefreshStreamURLs(Me, Settings.RefreshStreamURLsFromPLSAfter)
            ElseIf plp.Length > 0 Then ' Loading a playlist and not a file
              If IO.File.Exists(plp) Then
                Try
                  Dim plft As AvianPlayDB.PlaylistFileTable = Me._db.GetPlaylistFileDBData(plp)
                  Dim avpl As Playlists.AVPLPlaylist = Nothing

                  ' Abort a stream refresh (if one is going)
                  Me._currentPlaylist.AbortRefreshStreamURLs()

                  ' Clear the current playlist
                  Me._currentPlaylist.Entries.Clear()

                  ' Load the playlist
                  Select Case plft.Format
                    Case "AVPL"
                      avpl = Playlists.PlaylistReader.ParseAVPLFile(plp)
                    Case "M3U"
                      avpl = Playlists.AVPLPlaylist.FromStandardPlaylist(Playlists.PlaylistReader.ParseM3UFile(plp))
                    Case "PLS"
                      avpl = Playlists.AVPLPlaylist.FromStandardPlaylist(Playlists.PlaylistReader.ParsePLSFile(plp))
                    Case Else
                      Throw New Exception(GetLanguageString("UnknownPlaylistFormat"))
                  End Select

                  If avpl.Entries.Count > 0 Then
                    ' Which Now Playing playlist do we use?  (depends on if it's music, podcast, or radio) -- if we have mixed results, default is Radio -> Podcast -> Music
                    If plft.ContainsFiles Then
                      ' Default state
                      Me._currentPlaylist = Me.GetNowPlayingPlaylistByIndex(Me._podcastIndex)
                      Me._playingIndex = Me.GetNowPlayingIndexByIndex(Me._podcastIndex)

                      ' Loop through every item, looking for a music file (this means use music tab and not podcast tab)
                      For Each avpe As Playlists.AVPLPlaylistEntry In avpl.Entries
                        Dim ispc As Boolean? = Me._db.IsFilePodcastTristate(avpe.FilePath)
                        If Not ispc.HasValue OrElse ispc.Value = False Then
                          Me._currentPlaylist = Me.GetNowPlayingPlaylistByIndex(Me._musicIndex)
                          Me._playingIndex = Me.GetNowPlayingIndexByIndex(Me._musicIndex)
                          Exit For
                        End If
                      Next
                    ElseIf plft.ContainsStreams Then
                      Me._currentPlaylist = Me.GetNowPlayingPlaylistByIndex(Me._radioIndex)
                      Me._playingIndex = Me.GetNowPlayingIndexByIndex(Me._radioIndex)
                    End If

                    ' Load the items
                    For Each avpe As Playlists.AVPLPlaylistEntry In avpl.Entries
                      Me._currentPlaylist.Entries.Add(avpe)
                    Next

                    ' Set the refresh paramters
                    Me._currentPlaylist.LastFileIsQuery = False
                    Me._currentPlaylist.LastFileQueryWhereString = ""
                    Me._currentPlaylist.LastFileQueryWhereParameters = Nothing
                    Me._currentPlaylist.LastFileQueryOrder = ""
                    Me._currentPlaylist.LastFilePath = plp

                    If Me._currentPlaylist.Entries.Count > 0 Then
                      ' Stores the item that should be loaded and played (item #1 in this case)
                      Dim pple As AVPLPlaylistEntry = Me._currentPlaylist.Entries(0)

                      ' Load the selected song into the audio engine
                      If pple IsNot Nothing Then
                        Me.LoadPLEIntoAudioEngine(pple)
                        Me.ReshuffleCurrentPlaylist() ' When we are directly selecting and loading a new item, reshuffle the list so the current item becomes the 'start' item
                      End If

                      ' Jump to the appropriate tab
                      Me.SetSlidingButtonStripIndex(Me._playingIndex, False)

                      ' Press Play to start playing the selected song
                      Me.ProcessButtonPress(ControlFunctions.Functions.Play)

                      ' Start stream refresh thread (if applicable)
                      Me._currentPlaylist.RefreshStreamURLs(Me, Settings.RefreshStreamURLsFromPLSAfter)
                    Else ' Just in case...
                      MessageBox.Show(GetLanguageString("PlaylistEmptyNoLoad"))
                    End If
                  Else
                    MessageBox.Show(GetLanguageString("PlaylistEmptyNoLoad"))
                  End If
                Catch ex As Exception
                  Me.ShowOverlayTimedMessage(GetLanguageString("UnableToLoadPlaylist") & " " & ex.Message)
                  WriteToErrorLog("PrettyListMiddle_ItemClicked :: Unable to load playlist on library menu click :: " & ex.ToString)
                End Try
              Else
                Me.ShowOverlayTimedMessage(GetLanguageString("PlaylistFileNotFound"))
              End If
            ElseIf smk.Length > 0 Then
              Me._nextMenuBreadcrumb = Me._lmb.Items(idx).Text
              If Me._lmb.NextMenu(smk, Me._lmb.Items(idx)) Then
                Me._libraryBreadcrumb.Push(Me._nextMenuBreadcrumb)
                Me.BreadcrumbLabelMain.SetBreadcrumbTextFromStack(Me._libraryBreadcrumb)

                Me._libraryScrollPosition.Push(PrettyListMiddle.ScrollPosition)

                Me.PrettyListMiddle.Items = Me._lmb.Items
                Me.PrettyListMiddle.ScrollPosition = 0

                Me.MenuItemLeft.Enabled = True ' Enable the back button
              Else
                ' Next menu has resulted in an asynchronous call OR there is no progression of the menu.
              End If
            End If
          Else ' Index of the items is not consistent, so we need to refresh the list (this shouldn't happen, but hey...)
            WriteToErrorLog("PrettyListMiddle.ItemClicked :: LibraryItemInvalidError")
            MessageBox.Show(Language.GetLanguageString("LibraryItemInvalidError"), "Avian Play", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1)
            Me._lmb.RefreshMenu()
            Me.PrettyListMiddle.Items = Me._lmb.Items
          End If

        Case Me._musicIndex
          LoadAndPlayPLEFromNowPlayingList(pli, Me._musicPlaylist)
          If Settings.ShowControlsAfterNowPlayingSelect Then Me.NowPlayingListHide()
          Me.ReshuffleCurrentPlaylist() ' When we are directly selecting and loading a new item, reshuffle the list so the current item becomes the 'start' item

        Case Me._podcastIndex
          LoadAndPlayPLEFromNowPlayingList(pli, Me._podcastPlaylist)
          If Settings.ShowControlsAfterNowPlayingSelect Then Me.NowPlayingListHide()
          Me.ReshuffleCurrentPlaylist() ' When we are directly selecting and loading a new item, reshuffle the list so the current item becomes the 'start' item

        Case Me._radioIndex
          LoadAndPlayPLEFromNowPlayingList(pli, Me._radioPlaylist)
          If Settings.ShowControlsAfterNowPlayingSelect Then Me.NowPlayingListHide()
          Me.ReshuffleCurrentPlaylist() ' When we are directly selecting and loading a new item, reshuffle the list so the current item becomes the 'start' item

        Case Me._editorIndex
          Dim idx = Me._editorPlaylist.Entries.IndexOf(pli)
          If idx >= 0 Then
            Dim pee As New PlaylistEntryEditor()
            pee.Database = Me._db
            pee.PlaylistEntry = Me._editorPlaylist.Entries(idx)
            pee.SetColors(Skin.FormForegroundColor, Skin.FormBackgroundColor, Skin.FormBackgroundColorTextBox)
            If pee.ShowDialog = Windows.Forms.DialogResult.OK Then
              Me._editorPlaylist.Entries(idx).CopyInto(pee.PlaylistEntry)
              Me.PrettyListMiddle.Items = Me._editorPlaylist ' This will refresh thumbnails as a part of this action
              Me.SaveNPCacheFiles(CacheSaveItems.Editor)
            End If
          End If

        Case Me._settingsIndex
          Dim idx = Me._smb.Items.IndexOf(pli)
          If idx >= 0 Then
            Dim se As SettingsEntry = Me._smb.Items(idx)
            If Me._smb.Location = SettingsMenuBuilder.SettingsLocation.Sections Then
              Me._smb.LoadMenu(se.Key)
              Me._settingScrollPosition = Me.PrettyListMiddle.ScrollPosition
              Me.PrettyListMiddle.ScrollPosition = 0
              Me.PrettyListMiddle.Items = Me._smb.Items
              Me.MenuItemLeft.Enabled = True ' Enable the back button
              Me._settingsBreadcrumb.Push(se.Text)
              Me.BreadcrumbLabelMain.SetBreadcrumbTextFromStack(Me._settingsBreadcrumb)
            Else
              If se.Schema.SchemaType = SettingsEntry.SchemaSettings.SchemaTypeEnum.CONTROL_EDITOR Then
                Dim ce As New ControlEditor
                ce.BackColor = Skin.FormBackgroundColor
                ce.BackColorTextbox = Skin.FormBackgroundColorTextBox
                ce.ForeColor = Skin.FormForegroundColor
                ce.InactiveForeColor = Skin.FormForegroundInactiveColor
                ce.Surfaces = ControlContainerMain.Surfaces
                If ce.ShowDialog() = Windows.Forms.DialogResult.OK Then
                  Windows.Forms.Cursor.Current = Cursors.WaitCursor
                  ControlContainerMain.LoadControlsFromINI()
                  Me.RefreshPlaybackStates()
                  Windows.Forms.Cursor.Current = Cursors.Default
                End If
              Else
                Dim spe As New SettingsPropertyEditor(se)
                spe.BackColor = Skin.FormBackgroundColor
                spe.BackColorTextBox = Skin.FormBackgroundColorTextBox
                spe.ForeColor = Skin.FormForegroundColor

                If spe.ShowDialog <> Windows.Forms.DialogResult.Cancel Then
                  ' Set value in INI
                  If se.Schema.SchemaType = SettingsEntry.SchemaSettings.SchemaTypeEnum.FOLDERS Then
                    Settings.ValueListString(se.Schema.SectionKey, se.Schema.SettingsKey, ":") = spe.Value
                  ElseIf se.Schema.SchemaType = SettingsEntry.SchemaSettings.SchemaTypeEnum.STRING_LIST Then
                    Settings.ValueListString(se.Schema.SectionKey, se.Schema.SettingsKey, vbCr) = spe.Value
                  Else
                    Settings.ValueString(se.Schema.SectionKey, se.Schema.SettingsKey) = spe.Value
                  End If

                  ' Save INI
                  Settings.SaveSettings()

                  ' Change any running information on parts of Avian Play that could be directly affected by the change
                  If String.Compare(se.Schema.SectionKey, "podcasts", True) = 0 AndAlso String.Compare(se.Schema.SettingsKey, "PodcastSubscriptionInterval", True) = 0 Then
                    Me._pcm.IntervalHours = Settings.PodcastSubscriptionInterval
                  ElseIf String.Compare(se.Schema.SectionKey, "general", True) = 0 AndAlso String.Compare(se.Schema.SettingsKey, "SystemIdleTimerResetWhilePlaying", True) = 0 Then
                    Me._dsm.StopPreventDeviceSleep()
                    If Settings.SystemIdleTimerResetWhilePlaying Then
                      Me._dsm.StartPreventDeviceSleep("AvianPlay_Main_")
                    End If
                  ElseIf String.Compare(se.Schema.SettingsKey, "MonitorScannedPathsRealtime", True) = 0 Then
                    If Settings.MonitorScannedPathsRealtime Then
                      Me._libraryMonitor.StartRealtimeMonitor()
                    Else
                      Me._libraryMonitor.StopRealtimeMonitor()
                    End If
                  ElseIf String.Compare(se.Schema.SectionKey, "library", True) = 0 AndAlso (String.Compare(se.Schema.SettingsKey, "ScanPath", True) = 0 OrElse String.Compare(se.Schema.SettingsKey, "ExcludePath", True) = 0) Then
                    If Me._lastMonitorIsUpdating Then
                      Me._libraryRestartScan = True ' this flag will cause a scan restart when the scan is cancelled in the next step
                    End If
                    Me._libraryMonitor.StopAllMonitors()
                    Settings.RefreshScanAndExcludePathsOnNextPropertyGet()
                    Me._libraryMonitor.RescanMonitoredPaths()
                    If Settings.MonitorScannedPathsRealtime Then Me._libraryMonitor.StartRealtimeMonitor()
                  ElseIf String.Compare(se.Schema.SectionKey, "display", True) = 0 AndAlso String.Compare(Microsoft.VisualBasic.Left(se.Schema.SettingsKey, 3), "tab", True) = 0 Then
                    ' Set the visibility of the playback tabs
                    Me.SetVisibleTabs()
                  End If
                End If
              End If
            End If
          Else
            ' Menu is inconsistent... best to just do nothing, I guess...?
          End If
      End Select
    End If
  End Sub

  Private Sub PrettyListMiddle_FilterChanged(ByVal sender As Object, ByVal filter As String) Handles PrettyListMiddle.FilterChanged
    Playlists.AVPLPlaylistEntry.SearchTerm = Me.PrettyListMiddle.Filter
    Me.PrettyListMiddle.InvalidateSmart()
  End Sub

  Private Sub LoadAndPlayPLEFromNowPlayingList(ByVal pli As MobilePrettyGui.iPrettyListItem, ByVal nowplaying As AVPLPlaylist)
    For Each ple As AVPLPlaylistEntry In nowplaying
      If ple Is pli Then
        If ple Is AVPLPlaylistEntry.CurrentPlayingItem Then
          Me.ProcessButtonPress(ControlFunctions.Functions.Stop)
          Me.ProcessButtonPress(ControlFunctions.Functions.Play)
        Else
          Me.ProcessButtonPress(ControlFunctions.Functions.Stop)
          Me.LoadPLEIntoAudioEngine(ple)
          Me.ProcessButtonPress(ControlFunctions.Functions.Play)
        End If
        Exit For
      End If
    Next
  End Sub

  Private Sub LoadPLEIntoAudioEngine(ByVal ple As AVPLPlaylistEntry)
    If ple IsNot Nothing Then
      Me._noItemLoaded = False

      ' First, Make sure we are stopped and the file is closed!
      ProcessButtonPress(ControlFunctions.Functions.Stop)
      Me._AudioEngine.Close()

      ' Reset all the important status strings
      Me.PlaybackLabelTop.OverlayText = ""
      Me.PlaybackLabelTop.Artist = ""
      Me.PlaybackLabelTop.Title = ""
      Me.PlaybackLabelTop.TrackLength = 0
      Me.PlaybackLabelTop.TrackTime = 0
      ControlButton.TrackSliderMax = 0
      ControlButton.TrackSliderPosition = 0
      ControlContainerMain.InvalidateSmart()
      Me.PlaybackLabelTop.ShowMemorizeIcon = False
      Me.PlaybackLabelTop.StatusMessage = GetLanguageString("Loading")
      Me.UpdateTitle(Nothing, Nothing, Nothing, Nothing, False) ' This reset the "last track title" variables so that if you switch quickly to a new file/station and switch back, and the title is the same, the title information is not blank
      Me.UpdateStatusMessage()

      ' Reset additional status message file information
      Me._currentAlbumName = String.Empty
      Me._currentTrackNumber = 0

      ' Assign current playing item
      AVPLPlaylistEntry.CurrentPlayingItem = ple

      ' Figure out new playing index
      If AVPLPlaylistEntry.CurrentPlayingItem.Type <> AVPLPlaylistEntry.EntryType.Stream Then
        ' Check if file exists
        If IO.File.Exists(ple.FilePath) Then
          ' Podcast or Music?
          If Me._db.IsFilePodcast(ple.FilePath) Then
            Me._playingIndex = Me.GetNowPlayingIndexByIndex(Me._podcastIndex)
          Else
            Me._playingIndex = Me.GetNowPlayingIndexByIndex(Me._musicIndex)
          End If

          ' Fetch additional information (if available)
          Using aft As AvianPlayDB.AudioFileTable = Me._db.GetAudioFileAlbumAndTrackNumber(ple.FilePath)
            If aft IsNot Nothing Then
              Me._currentAlbumName = aft.Album.Trim
              Me._currentTrackNumber = aft.TrackNumber
            End If
          End Using

          ' Update the 'last play' date
          Me._db.UpdateLastPlayedDateToNow(ple.FilePath)
        Else
          ' Uh oh!  File is no longer here
          Me._db.SetAudioFileNotFoundStatus(ple.FilePath) = True
          Me.PlaybackLabelTop.StatusMessage = GetLanguageString("Stopped")
          Me.ShowOverlayTimedMessage(GetLanguageString("FileNotFound"))
          Me._noItemLoaded = True
          Me.RefreshPlaybackStates()
        End If
      Else
        Me._playingIndex = Me.GetNowPlayingIndexByIndex(Me._radioIndex)
      End If

      If Not Me._noItemLoaded Then
        ' Load the entry
        If AVPLPlaylistEntry.CurrentPlayingItem.IsFile Then
          If System.IO.File.Exists(AVPLPlaylistEntry.CurrentPlayingItem.FilePath) Then
            ' Assign the initial (playlist entry) artist/title information (we don't do this for streams because an overlay is set with the stream title before the subtitle becomes the artist/title)
            Me.PlaybackLabelTop.Artist = AVPLPlaylistEntry.CurrentPlayingItem.Artist
            Me.PlaybackLabelTop.Title = AVPLPlaylistEntry.CurrentPlayingItem.Title

            ' Load file into audio engine
            Me._AudioEngine.Open(AVPLPlaylistEntry.CurrentPlayingItem.FilePath)
          End If
        Else
          Me.LoadNextCurrentPlayingItemStreamURL()
        End If
      End If

      ' Refresh the playlist if it is visible
      If PrettyListMiddle.Visible Then
        Me.PrettyListMiddle.InvalidateSmart()
      End If
    Else
      Me._noItemLoaded = True
      Me.RefreshPlaybackStates()
      If PrettyListMiddle.Visible Then
        Me.PrettyListMiddle.InvalidateSmart()
      End If
    End If

    If Me._noItemLoaded Then
      Me.ShowOverlayTimedMessage("")
    End If

    Me.SetStatusMessageAltShow(False)
    Me.UpdateStatusMessage()
  End Sub

  Private Sub SaveToMemorizedSongList(ByVal artist As String, ByVal title As String, ByVal station As String, ByVal showMemorizeStatusMessages As Boolean)
    Dim tArtist As String = artist.Trim
    Dim tTitle As String = title.Trim
    Dim tStation As String = station.Trim

    If tTitle.Length <= 0 AndAlso tArtist.Length <= 0 Then
      If showMemorizeStatusMessages Then
        Me.ShowOverlayTimedMessage(GetLanguageString("MemorizeSongEmpty"))
      End If
    Else
      Dim ms As New MemorizedSongsList(Settings.MemorizedSongsPath)
      Dim song As New MemorizedSongsList.MemorizedItem
      song.Artist = tArtist
      song.Title = tTitle
      song.Station = tStation
      song.Time = Date.Now

      ' If there is a single entry, it should be title, not artist
      If song.Artist.Length > 0 AndAlso song.Title.Length <= 0 Then
        song.Title = song.Artist
        song.Artist = String.Empty
      End If

      If Not ms.CheckForDupe(song) Then
        If ms.AppendMemorizedSong(song) Then
          If showMemorizeStatusMessages Then
            Me.ShowOverlayTimedMessage(GetLanguageString("MemorizeSongSuccess"))
            Me.PlaybackLabelTop.ShowMemorizeIcon = True
          End If
        ElseIf showMemorizeStatusMessages Then
          Me.ShowOverlayTimedMessage(GetLanguageString("MemorizeSongError"))
        End If
      ElseIf showMemorizeStatusMessages Then
        Me.ShowOverlayTimedMessage(GetLanguageString("MemorizeSongDupe"))
      End If
    End If
  End Sub

  Private Sub LoadNextCurrentPlayingItemStreamURL(Optional ByVal specificIndex As Integer = -1)
    Static Dim lastPLE As AVPLPlaylistEntry = Nothing
    Dim newPle As Boolean = False

    If AVPLPlaylistEntry.CurrentPlayingItem IsNot Nothing Then
      If lastPLE IsNot AVPLPlaylistEntry.CurrentPlayingItem Then
        lastPLE = AVPLPlaylistEntry.CurrentPlayingItem
        Me._lastStreamURLIndex = 0
        newPle = True
      ElseIf specificIndex >= 0 Then
        Me._lastStreamURLIndex = specificIndex
      Else
        Me._lastStreamURLIndex += 1
      End If

      If Me._lastStreamURLIndex > AVPLPlaylistEntry.CurrentPlayingItem.StreamURLs.Count - 1 Then
        Me._lastStreamURLIndex = 0
      End If

      If AVPLPlaylistEntry.CurrentPlayingItem.StreamURLs.Count <= 0 Then
        Me._lastStreamURLIndex = 0
        Me.PlaybackLabelTop.OverlayText = GetLanguageString("LoadingStreamPlaylist")
        Try
          Windows.Forms.Cursor.Current = Cursors.WaitCursor
          Application.DoEvents() ' do this so the graphics are up-to-date at the time this pauses
          AVPLPlaylistEntry.CurrentPlayingItem.RefreshStreamURLsFromPlaylistURL()
        Catch ex As Threading.ThreadAbortException
          ' Ignore
        Catch ex As Exception
          ErrorLog.WriteToErrorLog("Unable to refresh stream URLs (LoadNextCurrentPlayingItemStreamURL) :: " & ex.ToString)
          Me.ShowOverlayTimedMessage(GetLanguageString("UnableToRefreshStreamURLs"))
        Finally
          Me.PlaybackLabelTop.OverlayText = ""
          Windows.Forms.Cursor.Current = Cursors.Default
        End Try
      End If

      ' See if we got any stream URLs or not
      If AVPLPlaylistEntry.CurrentPlayingItem.StreamURLs.Count > 0 Then
        Dim u As Uri = AVPLPlaylistEntry.CurrentPlayingItem.StreamURLs(Me._lastStreamURLIndex)
        Me.TimerStatusMessageFlip.Enabled = False
        Me.TimerStatusMessageFlip.Enabled = True
        Me._AudioEngine.StationTitle = AVPLPlaylistEntry.CurrentPlayingItem.StreamTitle
        Me._AudioEngine.Open(u)
        Me._noItemLoaded = False
        Me.SetStatusMessageAltShow(True)
        Me.UpdateStatusMessage()

        If Settings.ShowStreamLargeTitleOnLoad AndAlso newPle Then
          Me.ShowOverlayTimedMessage(AVPLPlaylistEntry.CurrentPlayingItem.StreamTitle)
        End If

        ' Tell the playlist entry which stream URL we are using, so that if it is saved, a working URL is stored as the first one to try
        AVPLPlaylistEntry.CurrentPlayingItem.SetFirstStreamURL(u)
      Else
        Me.ShowOverlayTimedMessage(GetLanguageString("NoValidStreamURLs"))
        Me.PlaybackLabelTop.StatusMessage = GetLanguageString("Stopped")
      End If
    End If
  End Sub




  Private Sub PrettyListMiddle_DrawOverlay(ByVal sender As Object, ByVal g As Graphics, ByVal width As Integer, ByRef height As Integer) Handles PrettyListMiddle.DrawOverlay
    Static brBg As New Drawing.SolidBrush(Skin.OverlayBackgroundColor)
    Static brFg As New Drawing.SolidBrush(Skin.OverlayTextColor)
    Static brBgNoMatch As New Drawing.SolidBrush(Skin.NoMatchBackgroundColor)
    Static brFgNoMatch As New Drawing.SolidBrush(Skin.NoMatchTextColor)
    Static fnt As New Drawing.Font("Tahoma", 6, FontStyle.Regular)

    Dim ht As Integer = 0

    Select Case Me._currentIndex
      Case Me._libraryIndex
        If Me._lastPodcastIsUpdating Then
          Dim updateStr As String
          If Me._lastPodcastIsDownloading Then
            updateStr = GetLanguageString("DownloadingPodcasts").Replace("%%", (Me._lastPodcastDownloadSize / 1024).ToString("#,0")).Replace("%", (Me._lastPodcastDownloadedBytes / 1024).ToString("#,0"))
          Else
            updateStr = GetLanguageString("UpdatingPodcasts")
          End If

          With g.MeasureString(updateStr, fnt)
            Dim lineHeight = .Height + (4 * Me._dpiFactor)
            Dim xPos As Integer = (width - .Width) >> 1

            If xPos < 0 Then xPos = 0
            ht += lineHeight

            g.FillRectangle(brBg, 0, height - ht, width, lineHeight)
            g.DrawString(updateStr, fnt, brFg, xPos, (height - ht) + (2 * Me._dpiFactor))
          End With
        End If

        If Me._libraryMonitor.IsMonitorOrRelatimeScanActive Then
          Dim updateString As String
          If Settings.ShowDetailedLibraryMonitorMessages Then
            updateString = Me._libraryMonitor.AdvancedNotificationString
          Else
            updateString = GetLanguageString("LibraryLoading")
          End If
          With g.MeasureString(updateString, fnt)
            Dim lineHeight = .Height + (4 * Me._dpiFactor)
            Dim xPos As Integer = (width - .Width) >> 1

            If xPos < 0 Then xPos = 0
            ht += lineHeight

            g.FillRectangle(brBg, 0, height - ht, width, lineHeight)
            g.DrawString(updateString, fnt, brFg, xPos, (height - ht) + (2 * Me._dpiFactor))
          End With
        End If
    End Select

    If Me.PrettyListMiddle.Filter.Length > 0 Then
      Dim filterString As String = GetLanguageString("FilteringList") & " " & Me.PrettyListMiddle.Filter
      With g.MeasureString(filterString, fnt)
        Dim lineHeight = .Height + (4 * Me._dpiFactor)
        Dim xPos As Integer = (width - .Width) >> 1

        If xPos < 0 Then xPos = 0
        ht += lineHeight

        g.FillRectangle(brBg, 0, height - ht, width, lineHeight)
        g.DrawString(filterString, fnt, brFg, xPos, (height - ht) + (2 * Me._dpiFactor))
      End With

      If Me.PrettyListMiddle.FilteredItems.Count <= 0 Then
        Dim nomatch As String = GetLanguageString("NoMatches")
        With g.MeasureString(nomatch, fnt)
          Dim lineHeight = .Height + (4 * Me._dpiFactor)
          Dim xPos As Integer = (width - .Width) >> 1

          If xPos < 0 Then xPos = 0
          ht += lineHeight

          g.FillRectangle(brBgNoMatch, 0, height - ht, width, lineHeight)
          g.DrawString(nomatch, fnt, brFgNoMatch, xPos, (height - ht) + (2 * Me._dpiFactor))
        End With
      End If
    End If

    height -= ht
  End Sub

  Private Sub Em_ExecuteControlFunction(ByVal func As Integer, ByVal arg As Integer, ByVal pressType As EventManager.ButtonActions, ByRef handled As Boolean) Handles _em.ExecuteControlFunction
    Dim isPlaying As Boolean = False
    Dim isPaused As Boolean = False

    Select Case Me._lastStatus
      Case MAP_STATUS.PAUSE
        isPlaying = False
        isPaused = True
      Case MAP_STATUS.PLAY
        isPlaying = True
        isPaused = False
      Case Else
        isPlaying = False
        isPaused = False
    End Select

    ' Break apart the multi
    Dim mfunc As ControlFunctions.Functions = ControlFunctions.GetControlFunctionFromMultiFunction(func, Me._lastIsReady, Me._lastIsWait, isPlaying, isPaused, Me._AudioEngine.IsStream, Me.PrettyListMiddle.Visible)

    ' Check function state to see if we process it
    If ControlFunctions.GetControlFunctionIsActive(mfunc, arg, Me._lastIsReady, Not Me._noItemLoaded, Me._lastIsWait, isPlaying, isPaused, Me._AudioEngine.IsStream, Me.PlaylistCountForControlFunctions) Then
      Select Case pressType
        Case EventManager.ButtonActions.Down
          Me.ProcessButtonDown(func, arg)
        Case EventManager.ButtonActions.Up
          Me.ProcessButtonUp(func, arg)
        Case Else
          Me.ProcessButtonPress(func, arg)
      End Select
      handled = True
    Else
      handled = False
    End If
  End Sub

  Private Sub Em_GetInList(ByRef inList As Boolean) Handles _em.GetInList
    inList = Me.PrettyListMiddle.Visible
  End Sub

  Private Sub Em_GetCurrentPlayState(ByRef curState As EventManager.ComparisonPlayStates) Handles _em.GetCurrentPlayState
    Select Case Me._lastStatus
      Case MAP_STATUS.PAUSE
        curState = EventManager.ComparisonPlayStates.Paused
      Case MAP_STATUS.PLAY
        curState = EventManager.ComparisonPlayStates.Playing
      Case Else
        curState = EventManager.ComparisonPlayStates.Stopped
    End Select
  End Sub

  Private Sub Em_GetCurrentItemType(ByRef curType As EventManager.ComparisonItemTypes) Handles _em.GetCurrentItemType
    If AVPLPlaylistEntry.CurrentPlayingItem IsNot Nothing Then
      If AVPLPlaylistEntry.CurrentPlayingItem.IsFile Then
        curType = EventManager.ComparisonItemTypes.File
      ElseIf AVPLPlaylistEntry.CurrentPlayingItem.IsStream Then
        curType = EventManager.ComparisonItemTypes.Stream
      Else
        curType = EventManager.ComparisonItemTypes.None
      End If
    Else
      curType = EventManager.ComparisonItemTypes.None
    End If
  End Sub




  Private Sub PreviousScene()
    Dim sc As Integer = Me.ControlContainerMain.CurrentSurface
    If sc >= 0 AndAlso sc <= 2 Then
      Dim csi As Integer = Me.ControlContainerMain.Surfaces(sc).CurrentSceneIndex
      If csi > 0 Then
        Me.ControlContainerMain.Surfaces(sc).CurrentSceneIndex = csi - 1
      Else
        Me.ControlContainerMain.Surfaces(sc).CurrentSceneIndex = Me.ControlContainerMain.Surfaces(sc).Scenes.Count - 1
      End If
      Me.ControlContainerMain.InvalidateSmart()
    End If
  End Sub

  Private Sub NextScene()
    Dim sc As Integer = Me.ControlContainerMain.CurrentSurface
    If sc >= 0 AndAlso sc <= 2 Then
      Dim csi As Integer = Me.ControlContainerMain.Surfaces(sc).CurrentSceneIndex
      If csi < Me.ControlContainerMain.Surfaces(sc).Scenes.Count - 1 Then
        Me.ControlContainerMain.Surfaces(sc).CurrentSceneIndex = csi + 1
      Else
        Me.ControlContainerMain.Surfaces(sc).CurrentSceneIndex = 0
      End If
      Me.ControlContainerMain.InvalidateSmart()
    End If
  End Sub

  Public Function CanPreviousOrNextScene() As Boolean
    Return Me.ControlContainerMain.Surfaces(Me.ControlContainerMain.CurrentSurface).Scenes.Count > 1
  End Function


  Private Delegate Sub lmb_CallAsyncPluginAssignItemsOnMainThreadDelegate()
  Private Sub lmb_CallAsyncPluginAssignItemsOnMainThread()
    Try
      If Me.InvokeRequired Then
        Dim dl As New lmb_CallAsyncPluginAssignItemsOnMainThreadDelegate(AddressOf lmb_CallAsyncPluginAssignItemsOnMainThread)
        Me.Invoke(dl)
      Else
        If Me._lmb.AssignPluginItems() Then
          Me._libraryBreadcrumb.Push(Me._nextMenuBreadcrumb)
          Me.BreadcrumbLabelMain.SetBreadcrumbTextFromStack(Me._libraryBreadcrumb)

          Me._libraryScrollPosition.Push(PrettyListMiddle.ScrollPosition)
          Me.PrettyListMiddle.ScrollPosition = 0

          Me.PrettyListMiddle.Items = Me._lmb.Items

          Me.MenuItemLeft.Enabled = True ' Enable the back button
        End If
      End If
    Catch ex As Exception
      WriteToErrorLog("LIBRARY PLUGIN :: CallAsyncPluginAssignItemsOnMainThread :: " & ex.ToString)
      MessageBox.Show(GetLanguageString("LibraryPluginItemAssignmentError"), "Avian Play", MessageBoxButtons.OK, MessageBoxIcon.Hand, MessageBoxDefaultButton.Button1)
    End Try
  End Sub

  Private Sub lmb_SetListEnabled(ByVal enabled As Boolean)
    SyncLock Me.PrettyListMiddle ' We synclock to make sure async operations on the library menu builder doesn't set up the 'enabled' property out of order
      If Me._currentIndex = Me._libraryIndex Then
        Me.PrettyListMiddle.Enabled = enabled
      End If
      Me._libraryIsEnabled = enabled
    End SyncLock
  End Sub

  Private Sub TimerOverlay_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TimerOverlay.Tick
    Me.TimerOverlay.Enabled = False
    If Me._noItemLoaded Then
      Me.PlaybackLabelTop.OverlayText = GetLanguageString("NoItemLoaded")
    Else
      Me.PlaybackLabelTop.OverlayText = ""
    End If
  End Sub

  Private Delegate Sub Pcm_DownloadThreadStatusChangedDelegate(ByVal isRunning As Boolean, ByVal isDownloading As Boolean, ByVal bytesRead As Integer, ByVal bytesSize As Integer)
  Private Sub Pcm_DownloadThreadStatusChanged(ByVal isRunning As Boolean, ByVal isDownloading As Boolean, ByVal bytesRead As Integer, ByVal bytesSize As Integer) Handles _pcm.DownloadThreadStatusChanged
    If Me.InvokeRequired Then
      Dim dl As New Pcm_DownloadThreadStatusChangedDelegate(AddressOf Pcm_DownloadThreadStatusChanged)
      Me.Invoke(dl, New Object() {isRunning, isDownloading, bytesRead, bytesSize})
    Else
      Me._lastPodcastIsUpdating = isRunning
      Me._lastPodcastIsDownloading = isDownloading
      Me._lastPodcastDownloadedBytes = bytesRead
      Me._lastPodcastDownloadSize = bytesSize

      If Me._currentIndex = Me._libraryIndex Then
        Me.SetMenuLibrary(Me.MenuItemRight.MenuItems)
        Me.PrettyListMiddle.InvalidateSmart()
      End If
    End If
  End Sub

  Private Delegate Sub Pcm_FileIsDownloadedDelegate(ByVal filePath As String)
  Private Sub Pcm_FileIsDownloaded(ByVal filePath As String) Handles _pcm.FileIsDownloaded
    If Me.InvokeRequired Then
      Dim fid As New Pcm_FileIsDownloadedDelegate(AddressOf Pcm_FileIsDownloaded)
      Me.Invoke(fid, New Object() {filePath})
    Else
      Me._libraryMonitor.UpdateSingleFile(filePath)
    End If
  End Sub


  Private Sub PrettyListMiddleHide()
    Me.PrettyListMiddle.Visible = False ' Note: don't set the 'enabled' property because it's used to set the dimming visibility of this control; visible is sufficient
    Me.PrettyListMiddle.SendToBack()
    Me.ControlContainerMain.BringToFront()
    Me.ControlContainerMain.Enabled = True
    Me.ControlContainerMain.Visible = True
    Me.BreadcrumbLabelMain.Enabled = False
    Me.BreadcrumbLabelMain.Visible = False
  End Sub

  Private Sub PrettyListMiddleShow()
    Me.PrettyListMiddle.Visible = True ' Note: don't set the 'enabled' property because it's used to set the dimming visibility of this control; visible is sufficient
    Me.PrettyListMiddle.BringToFront()
    Me.ControlContainerMain.SendToBack()
    Me.ControlContainerMain.Enabled = False
    Me.ControlContainerMain.Visible = False

    ' Show breadcrumb on the appropriate tabs only
    If Me._currentIndex = Me._libraryIndex OrElse Me._currentIndex = Me._settingsIndex Then
      Me.BreadcrumbLabelMain.Enabled = True
      Me.BreadcrumbLabelMain.Visible = True
    Else
      Me.BreadcrumbLabelMain.Enabled = False
      Me.BreadcrumbLabelMain.Visible = False
    End If
  End Sub

  Private Sub NowPlayingListHide()
    Me.PrettyListMiddleHide()

    Select Case Me._currentIndex
      Case Me._musicIndex, Me._podcastIndex, Me._radioIndex
        Me.PlaybackLabelTop.SmallMode = Me._lastSmallLabel
    End Select

    Me.UpdateBottomMenu()
  End Sub

  Private Sub NowPlayingListShow()
    Select Case Me._currentIndex
      Case Me._musicIndex, Me._podcastIndex, Me._radioIndex
        If Me._currentIndex = Me._musicIndex Then
          Me.PrettyListMiddle.Items = Me._musicPlaylist
        ElseIf Me._currentIndex = Me._podcastIndex Then
          Me.PrettyListMiddle.Items = Me._podcastPlaylist
        ElseIf Me._currentIndex = Me._radioIndex Then
          Me.PrettyListMiddle.Items = Me._radioPlaylist
        End If

        Me.PlaybackLabelTop.SmallMode = True
        Me.PrettyListMiddleShow()
      Case Else
        Me.NowPlayingListShow()
    End Select

    Me.UpdateBottomMenu()
  End Sub


  Protected Overrides Sub OnKeyDown(ByVal e As System.Windows.Forms.KeyEventArgs)
    e.Handled = Me._em.OnKeyDown(e.KeyCode, Me._keyDownHandled)
    Me._keyDownHandled = e.Handled

    MyBase.OnKeyDown(e)
  End Sub

  Protected Overrides Sub OnKeyPress(ByVal e As System.Windows.Forms.KeyPressEventArgs)
    e.Handled = False
    If Not Me._keyDownHandled Then
      ' Pass the keypress down to the LMB to filter the list, then assign it back to the list

      If PrettyListMiddle.Visible = True Then
        Me.PrettyListMiddle.AddFilterKey(e.KeyChar)
        e.Handled = True
      End If
    End If
    Me._keyPressHandled = e.Handled
    MyBase.OnKeyPress(e)
  End Sub

  Protected Overrides Sub OnKeyUp(ByVal e As System.Windows.Forms.KeyEventArgs)
    If Me._keyDownHandled AndAlso Not Me._keyPressHandled AndAlso Me._em.OnKeyUp(e.KeyCode) Then
      e.Handled = True
    End If

    Me._keyDownHandled = False
    Me._keyPressHandled = False

    MyBase.OnKeyUp(e)
  End Sub




  Private Sub InputPanelMain_EnabledChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles InputPanelMain.EnabledChanged
    If Me._isFinishedLoading Then
      Static lastSIPState As Boolean = Not Me.InputPanelMain.Enabled ' So that the first time through always executes, we set it to NOT enabled

      If Me.InputPanelMain.Enabled Then
        ' Always reset this since other factors can mess up the sliding strip height (not sure why, but it has happened)
        Me.SlidingButtonStripMain.Enabled = False
        Me.SlidingButtonStripMain.Height = Me.InputPanelMain.Bounds.Height

        ' Only reset the top label when the state changes
        If lastSIPState <> Me.InputPanelMain.Enabled Then Me.PlaybackLabelTop.SmallMode = True
      Else
        ' Always reset this since other factors can mess up the sliding strip height (not sure why, but it has happened)
        Me.SlidingButtonStripMain.Enabled = True
        Me.SlidingButtonStripMain.Height = Me._slidingStripHeight

        ' Only reset the top label when the state changes
        If lastSIPState <> Me.InputPanelMain.Enabled AndAlso Not Me.PrettyListMiddle.Visible AndAlso Me.ControlContainerMain.CurrentSurface >= 0 Then
          Me.PlaybackLabelTop.SmallMode = Me._lastSmallLabel
        End If
      End If

      lastSIPState = Me.InputPanelMain.Enabled
    End If
  End Sub

  Private Sub Me_Resize(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Resize
    Me.InputPanelMain_EnabledChanged(sender, EventArgs.Empty) ' We have to reevaluate if the SIP is deployed or not because the SIP state may change when the screen rotates and if focus goes wacky (precautionary)
    If Me.PrettyListMiddle.Visible Then
      Me.PrettyListMiddle.InvalidateSmart()
    Else
      Me.ControlContainerMain.InvalidateSmart()
    End If
  End Sub

  Private Sub Me_Activate(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Activated
    Me.InputPanelMain_EnabledChanged(sender, EventArgs.Empty) ' We have to reevaluate if the SIP is deployed or not because the SIP state may change while the app is in the background
  End Sub

  Private Sub TimerTrackCounter_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TimerTrackCounter.Tick
    If Me._AudioEngine.IsStream Then
      Me.PlaybackLabelTop.TrackTime = CInt(Me._streamStopWatch.ElapsedMilliseconds / 1000)
      Me.PlaybackLabelTop.TrackTimeAlternate = CInt(Me._streamSongStopWatch.ElapsedMilliseconds / 1000)
      ControlButton.TrackSliderPosition = 0
      ControlButton.TrackSliderMax = 0
    ElseIf Me._lastStatus = MAP_STATUS.PLAY Then
      Me._AudioEngine.UpdateTrackPosition()
      Me.PlaybackLabelTop.TrackTimeAlternate = -1
    End If
  End Sub

  Private Sub AudioEngine_TrackTimer(ByVal seconds As Integer) Handles _AudioEngine.TrackTimer
    Me.PlaybackLabelTop.TrackTime = seconds

    ' We should only update this value when the track slider is NOT being manipulated
    If Not Me.TimerSeekSet.Enabled Then
      ControlButton.TrackSliderPosition = seconds
    End If

    ' Update the overlay if we are FF or REW
    If Me.TimerFwdRewind.Enabled Then
      Me.PlaybackLabelTop.OverlayTextHuge = PlaybackLabel.GetFormattedTimerString(ControlButton.TrackSliderPosition, ControlButton.TrackSliderMax)
    End If

    Me.ControlContainerMain.InvalidateSmart()
  End Sub

  Private Sub TimerStatusMessageFlip_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TimerStatusMessageFlip.Tick
    Me._showStatusMessageAlt = Not Me._showStatusMessageAlt
    Me.UpdateStatusMessage()
  End Sub

  Private Sub SetStatusMessageAltShow(ByVal show As Boolean)
    Me._showStatusMessageAlt = show
    Me.TimerStatusMessageFlip.Enabled = False
    Me.TimerStatusMessageFlip.Enabled = True
  End Sub

  Private Sub ShowOverlayTimedMessage(ByVal msg As String)
    Me.TimerOverlay.Enabled = False
    Me.PlaybackLabelTop.OverlayText = msg
    Me.TimerOverlay.Enabled = True
  End Sub

  Private Sub TimerUpdateTrackPositionInDB_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TimerUpdateTrackPositionInDB.Tick
    If Me._lastStatus = MAP_STATUS.PLAY AndAlso Me.PlaybackLabelTop.TrackTime > 0 AndAlso AVPLPlaylistEntry.CurrentPlayingItem IsNot Nothing AndAlso AVPLPlaylistEntry.CurrentPlayingItem.FilePath.Length > 0 Then
      Me._db.UpdateLastPlayPosition(AVPLPlaylistEntry.CurrentPlayingItem.FilePath, Me.PlaybackLabelTop.TrackTime)
    End If
  End Sub

  Private Sub TimerFwdRewind_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TimerFwdRewind.Tick
    If Me._AudioEngine.LastButton <> MAP_STATUS.PLAY OrElse Me._lastStatus <> MAP_STATUS.PLAY Then
      Me.TimerFwdRewind.Enabled = False ' A fail-safe for script or something else causing play to stop unexpectedly (also can happen during fwd/rew over between tracks)
      Me.ControlContainerMain.ClearButtonStates() ' We should clear out the buttons too to prevent a ButtonUp of fwd/rew activating a next track/previous track if a new track is loading
      Me.ControlContainerMain.InvalidateSmart()
    Else
      If Me._forwardRewindIsRewind Then
        ' Rewind
        Me._AudioEngine.SkipBack(Settings.RewSecondsPerTick)
      Else
        ' Forward
        Me._AudioEngine.SkipAhead(Settings.FwdSecondsPerTick)
      End If
      Me.PlaybackLabelTop.OverlayTextHuge = PlaybackLabel.GetFormattedTimerString(ControlButton.TrackSliderPosition, ControlButton.TrackSliderMax)
    End If
  End Sub

  Public Sub PlaybackLabelTop_DoubleClick(ByVal sender As Object, ByVal e As EventArgs) Handles PlaybackLabelTop.DoubleClick
    ' >=0 because tabs that only have a list have a Surface value of -1 ; only fetch a new "small mode" if the now playing list is not visible
    If Me.ControlContainerMain.CurrentSurface >= 0 AndAlso Not Me.PrettyListMiddle.Visible Then
      Me._lastSmallLabel = Me.PlaybackLabelTop.SmallMode
    End If
  End Sub

  Private Function ActiveNowPlayingList() As Playlists.AVPLPlaylist
    Dim npList As Playlists.AVPLPlaylist = Nothing

    Select Case Me._currentIndex
      Case Me._musicIndex
        npList = Me._musicPlaylist
      Case Me._podcastIndex
        npList = Me._podcastPlaylist
      Case Me._radioIndex
        npList = Me._radioPlaylist
    End Select

    Return npList
  End Function

  Private Sub ClearItemsInCurrentNowPlayingList()
    Dim npList As Playlists.AVPLPlaylist = Me.ActiveNowPlayingList

    If npList IsNot Nothing Then
      ' Find and remove the items
      Dim removeList As New List(Of Playlists.AVPLPlaylistEntry)
      For Each ple As Playlists.AVPLPlaylistEntry In npList.Entries
        If ple IsNot Playlists.AVPLPlaylistEntry.CurrentPlayingItem Then
          removeList.Add(ple)
        End If
      Next
      For Each ple As Playlists.AVPLPlaylistEntry In removeList
        npList.Entries.Remove(ple)
      Next

      ' Reset the list
      Me.PrettyListMiddle.Items = npList
      Me.SaveNPCacheFiles(npList)

      ' Refresh play back states
      Me.RefreshPlaybackStates()
    End If
  End Sub

  Private Sub RemoveItemFromNowPlayingList(ByVal avple As AVPLPlaylistEntry)
    If avple IsNot Nothing AndAlso Not Playlists.AVPLPlaylistEntry.CurrentPlayingItem Is avple Then
      Dim npList As Playlists.AVPLPlaylist = Me.ActiveNowPlayingList

      If npList IsNot Nothing Then
        ' Remove the item
        npList.Entries.Remove(avple)

        ' Reset the list
        Me.PrettyListMiddle.Items = npList
        Me.SaveNPCacheFiles(npList)

        ' Refresh play back states
        Me.RefreshPlaybackStates()
      End If
    End If
  End Sub

  Private Sub TimerSeekSet_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TimerSeekSet.Tick
    If Not Me._AudioEngine.IsStream Then
      ' Since we are previewing here, make sure we don't go beyond the end of the track during the previous
      Dim seekTo As Integer = ControlButton.TrackSliderPosition

      If seekTo + (Me.TimerSeekSet.Interval / 1000) + 1 > ControlButton.TrackSliderMax Then
        seekTo = ControlButton.TrackSliderMax - ((Me.TimerSeekSet.Interval / 1000) + 1)
      ElseIf seekTo < 0 Then
        seekTo = 0
      End If

      Me._AudioEngine.Seek(seekTo)
    End If
  End Sub

  Private Sub ReshuffleCurrentPlaylist()
    ' Reshuffle the entire current playing playlist, with the starting point being the currently loaded file
    If Me._menuItemControlShuffle.Checked Then
      If Me._currentPlaylist IsNot Nothing AndAlso Playlists.AVPLPlaylistEntry.CurrentPlayingItem IsNot Nothing Then
        If Me._playingIndex = Me._musicIndex OrElse Me._playingIndex = Me._podcastIndex Then
          Me._currentPlaylist.ReShuffle(Playlists.AVPLPlaylistEntry.CurrentPlayingItem.GetHashCode)
        End If
      End If
    End If
  End Sub

  Private ReadOnly Property PlaylistCountForControlFunctions() As Integer
    Get
      Dim lst As AVPLPlaylist = ActiveNowPlayingList()

      If lst IsNot Nothing Then
        Return lst.Count
      End If

      Return 0
    End Get
  End Property

  Private Sub ShowVolumeForm()
    Dim v As New Volume
    v.SetImages(Skin.VolumeForm_BitmapEmpty, Skin.VolumeForm_BitmapFull)
    v.BackColor = Skin.VolumeBackgroundColor
    v.Volume = Me.Volume
    v.Mute = Me.Mute
    AddHandler v.VolumeChanged, AddressOf VolumeForm_VolumeChanged
    AddHandler v.MuteChanged, AddressOf VolumeForm_MuteChanged
    Try
      v.ShowDialog()
    Finally
      RemoveHandler v.VolumeChanged, AddressOf VolumeForm_VolumeChanged
      RemoveHandler v.MuteChanged, AddressOf VolumeForm_MuteChanged
    End Try
  End Sub

  Private Sub VolumeForm_VolumeChanged(ByVal level As Byte)
    Me.Volume = level
  End Sub

  Private Sub VolumeForm_MuteChanged(ByVal enabled As Boolean)
    Me.Mute = enabled
  End Sub

  ''' <summary>
  ''' USE THIS TO SET VOLUME!!  It updates everything that concerns itself with volume
  ''' </summary>
  Public Property Volume() As Byte
    Get
      Return ControlButton.VolumePercent
    End Get
    Set(ByVal value As Byte)
      If value > 100 Then value = 100
      If value < 0 Then value = 0
      ControlButton.VolumePercent = value
      Me._AudioEngine.SetVolume(value)
      Me.ControlContainerMain.InvalidateSmart()
      If Me.Mute Then Me.Mute = False ' If we are muted, unmute
    End Set
  End Property

  ''' <summary>
  ''' USE THIS TO SET MUTE!!  It updates everything that concerns itself with muting
  ''' </summary>
  Public Property Mute() As Boolean
    Get
      Return ControlButton.Mute
    End Get
    Set(ByVal value As Boolean)
      ControlButton.Mute = value
      Me._AudioEngine.SetMute(value)
      Me.ControlContainerMain.InvalidateSmart()
    End Set
  End Property

  Private Function LoadIntoEditorPlaylist(ByVal src As AVPLPlaylist) As Boolean
    If src.Count <= 0 Then
      MessageBox.Show(GetLanguageString("EditorLoadListIsEmpty"), "Avian Play")
      Return False
    Else
      If Me._editorPlaylist.Count > 0 AndAlso MessageBox.Show(GetLanguageString("EditorClearCurrentList"), "Avian Play", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) = Windows.Forms.DialogResult.Yes Then
        Me._editorPlaylist.Entries.Clear()
      End If

      For Each avple As AVPLPlaylistEntry In src.Entries
        Dim newple As New AVPLPlaylistEntry(avple)
        newple.IgnoreLoadedItem = True
        Me._editorPlaylist.Entries.Add(newple)
      Next

      If Me._currentIndex = Me._editorIndex Then
        Me.PrettyListMiddle.Items = Me._editorPlaylist
        Me.SaveNPCacheFiles(CacheSaveItems.Editor)
      End If
    End If

    Return True
  End Function

  Private Sub LoadFromEditorPlaylist(ByVal dest As AVPLPlaylist)
    If dest.Count > 0 AndAlso MessageBox.Show(GetLanguageString("EditorClearDestinationList"), "Avian Play", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) = Windows.Forms.DialogResult.Yes Then
      dest.Entries.Clear()
    End If

    For Each avple As AVPLPlaylistEntry In Me._editorPlaylist.Entries
      Dim newple As New AVPLPlaylistEntry(avple)
      newple.IgnoreLoadedItem = False
      dest.Entries.Add(newple)
    Next

    Me.SaveNPCacheFiles(dest)
  End Sub

  Private Sub SaveNPCacheFiles(ByRef avpl As Playlists.AVPLPlaylist)
    If avpl Is Me._musicPlaylist Then
      Me.SaveNPCacheFiles(CacheSaveItems.Music)
    ElseIf avpl Is Me._podcastPlaylist Then
      Me.SaveNPCacheFiles(CacheSaveItems.Podcast)
    ElseIf avpl Is Me._radioPlaylist Then
      Me.SaveNPCacheFiles(CacheSaveItems.Radio)
    ElseIf avpl Is Me._editorPlaylist Then
      Me.SaveNPCacheFiles(CacheSaveItems.Editor)
    End If
  End Sub

  Private Sub SaveNPCacheFiles(ByVal item As CacheSaveItems)
    If Settings.SaveNPCache AndAlso Settings.UpdateNPCacheAsListUpdated Then
      Me._cacheSaveQueue.Enqueue(item)

      If Me._cacheSaveThread Is Nothing Then
        Me._cacheSaveThread = New Threading.Thread(AddressOf SaveNPCacheFilesThread)
        Me._cacheSaveThread.Name = "CacheSaveThread"
        Me._cacheSaveThread.Priority = Threading.ThreadPriority.Lowest
        SyncLock MainThreadPool
          MainThreadPool.Add(Me._cacheSaveThread)
        End SyncLock
        Me._cacheSaveThread.Start()
      End If
    End If
  End Sub

  Private Sub SaveNPCacheFilesThread()
    Try
      While Me._cacheSaveQueue.Count > 0 AndAlso Not Me._cacheSaveQuit
        Dim item As CacheSaveItems = Me._cacheSaveQueue.Dequeue
        Select Case item
          Case CacheSaveItems.Music
            Me._musicPlaylist.SaveToFile(Settings.NowPlayingMusicPath)
          Case CacheSaveItems.Podcast
            Me._podcastPlaylist.SaveToFile(Settings.NowPlayingPodcastPath)
          Case CacheSaveItems.Radio
            Me._radioPlaylist.SaveToFile(Settings.NowPlayingRadioPath)
          Case CacheSaveItems.Editor
            Me._editorPlaylist.SaveToFile(Settings.EditorPath)
        End Select
      End While
    Catch ex As Threading.ThreadAbortException
      ' Ignore
    Catch ex As Exception
      Me.WriteErrorOnMainThread("AvianPlayMain.SaveNPCacheFilesThread :: " & ex.ToString)
    Finally
      Me._cacheSaveQuit = False
      SyncLock MainThreadPool
        MainThreadPool.Remove(Me._cacheSaveThread)
      End SyncLock
      Me._cacheSaveThread = Nothing
    End Try
  End Sub

  Private Sub CheckMemorizeThread()
    Dim myRef As Threading.Thread = Me._checkMemorizeThread ' Cache this here because this reference may change before the thread finishes and releases itself from the thread pool
    Try
      Dim item As New MemorizedSongsList.MemorizedItem
      item.Artist = Me._lastArtist
      item.Title = Me._lastTitle

      ' If only a single entry is filled, it should always be the title, not the artist, so flip it
      If item.Title.Length <= 0 AndAlso item.Artist.Length > 0 Then
        item.Title = item.Artist
        item.Artist = String.Empty
      End If

      If item.Artist.Length > 0 OrElse item.Title.Length > 0 Then
        Dim mem As New MemorizedSongsList(Settings.MemorizedSongsPath)
        Me.SetMemorizeIcon(mem.CheckForDupe(item))
      Else
        Me.SetMemorizeIcon(False)
      End If
    Catch ex As Threading.ThreadAbortException
      Me.SetMemorizeIcon(False)
    Catch ex As Exception
      WriteErrorOnMainThread("AvianPlayMain.CheckMemorize :: " & ex.ToString)
      Me.SetMemorizeIcon(False)
    Finally
      If myRef IsNot Nothing Then
        SyncLock MainThreadPool
          MainThreadPool.Remove(myRef)
        End SyncLock
      End If
    End Try
  End Sub

  Private Delegate Sub SetMemorizeIconDelegate(ByVal value As Boolean)
  Private Sub SetMemorizeIcon(ByVal value As Boolean)
    If PlaybackLabelTop.InvokeRequired Then
      Dim sid As New SetMemorizeIconDelegate(AddressOf SetMemorizeIcon)
      Me.PlaybackLabelTop.Invoke(sid, New Object() {value})
    Else
      Me.PlaybackLabelTop.ShowMemorizeIcon = value
    End If
  End Sub

  Private Sub TimerLongPressAnimation_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TimerLongPressAnimation.Tick
    Dim elapsed As Long = Me._longPressStopWatch.ElapsedMilliseconds
    Dim grace As Long = Settings.LongPressTime / 5
    Dim fillGrace As Long = grace + (grace / 4)

    If elapsed > Settings.LongPressTime Then
      Me.TimerLongPressAnimation.Enabled = False
      Me._longPressStopWatch.Reset()
      Me.PlaybackLabelTop.LongPressPercentage = 0.0F
    ElseIf elapsed >= grace Then
      Me.PlaybackLabelTop.LongPressPercentage = CSng(elapsed - grace) / CSng(Settings.LongPressTime - fillGrace)
    End If
  End Sub

  Private Sub OpenFileIntoNowPlaying()
    Dim npl As AVPLPlaylist = ActiveNowPlayingList()
    If npl IsNot Nothing Then
      Dim ple As Playlists.AVPLPlaylistEntry = FileSelectionDialogs.GetAudioFile(Me._db)
      If ple IsNot Nothing Then
        npl.Entries.Add(ple)
        Me.PrettyListMiddle.Items = npl
        Me.SaveNPCacheFiles(npl)
      End If
    End If
  End Sub

  Private Sub OpenPlaylistIntoNowPlaying()
    Dim npl As AVPLPlaylist = ActiveNowPlayingList()
    If npl IsNot Nothing Then
      Dim avpl As Playlists.AVPLPlaylist = FileSelectionDialogs.GetPlaylistFile()
      If avpl IsNot Nothing Then
        For Each ape As Playlists.AVPLPlaylistEntry In avpl
          npl.Entries.Add(ape)
          Me.PrettyListMiddle.Items = npl
          Me.SaveNPCacheFiles(npl)
        Next
      End If
    End If
  End Sub

  Private Sub OpenURLIntoNowPlaying()
    Dim npl As AVPLPlaylist = ActiveNowPlayingList()
    If npl IsNot Nothing Then
      Dim avple As New Playlists.AVPLPlaylistEntry
      avple.Type = AVPLPlaylistEntry.EntryType.Stream

      Dim pee As New PlaylistEntryEditor()
      pee.Database = Me._db
      pee.PlaylistEntry = avple
      pee.SetColors(Skin.FormForegroundColor, Skin.FormBackgroundColor, Skin.FormBackgroundColorTextBox)
      If pee.ShowDialog = Windows.Forms.DialogResult.OK Then
        npl.Entries.Add(pee.PlaylistEntry)
        Me.PrettyListMiddle.Items = npl
        Me.SaveNPCacheFiles(npl)
      End If
    End If
  End Sub

  Private Sub SetVisibleTabs()
    Me._buttonStripItems(0).Visible = Settings.TabMusicVisible
    Me._buttonStripItems(1).Visible = Settings.TabPodcastsVisible
    Me._buttonStripItems(2).Visible = Settings.TabRadioVisible
    Me.SlidingButtonStripMain.ResnapToCurrentIndex()
  End Sub

  Public ReadOnly Property IsMusicTabVisible() As Boolean
    Get
      Return Me._buttonStripItems(0).Visible
    End Get
  End Property

  Public ReadOnly Property IsPodcastTabVisible() As Boolean
    Get
      Return Me._buttonStripItems(1).Visible
    End Get
  End Property

  Public ReadOnly Property IsRadioTabVisible() As Boolean
    Get
      Return Me._buttonStripItems(2).Visible
    End Get
  End Property

  Private Function GetMusicTabIndex() As Integer
    If Settings.TabMusicVisible Then
      Return Me._musicIndex
    ElseIf Settings.TabPodcastsVisible Then
      Return Me._podcastIndex
    Else
      Return Me._radioIndex
    End If
  End Function

  Private Function GetPodcastsTabIndex() As Integer
    If Settings.TabPodcastsVisible Then
      Return Me._podcastIndex
    ElseIf Settings.TabMusicVisible Then
      Return Me._musicIndex
    Else
      Return Me._radioIndex
    End If
  End Function

  Private Function GetRadioTabIndex() As Integer
    If Settings.TabRadioVisible Then
      Return Me._radioIndex
    ElseIf Settings.TabMusicVisible Then
      Return Me._musicIndex
    Else
      Return Me._podcastIndex
    End If
  End Function

  Private Function GetNowPlayingPlaylistByIndex(ByVal idx As Integer) As AVPLPlaylist
    Select Case Me.GetNowPlayingIndexByIndex(idx)
      Case Me._radioIndex
        Return Me._radioPlaylist
      Case Me._podcastIndex
        Return Me._podcastPlaylist
      Case Else
        Return Me._musicPlaylist
    End Select
  End Function

  Private Function GetNowPlayingIndexByIndex(ByVal idx As Integer) As Integer
    Select Case idx
      Case Me._radioIndex
        Return Me.GetRadioTabIndex
      Case Me._podcastIndex
        Return Me.GetPodcastsTabIndex
      Case Else
        Return Me.GetMusicTabIndex
    End Select
  End Function


End Class
